diff --git a/README.md b/README.md index 7663953..1883dda 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,11 @@ * POST /v1/billing/subscriptions/:id/capture * POST /v1/billing/subscriptions/:id/suspend * GET /v1/billing/subscriptions/:id/transactions + +### Invoicing + +* POST /v2/invoicing/generate-next-invoice-number +* GET /v2/invoicing/invoices/:id ## Missing endpoints @@ -371,6 +376,27 @@ c.DeleteWebhook("WebhookID") c.ListWebhooks(paypal.AncorTypeApplication) ``` +### Generate Next Invoice Number +```go +// GenerateInvoiceNumber: generates the next invoice number that is available to the merchant. +c.GenerateInvoiceNumber(ctx) // might return something like "0001" or "0010". +``` + +### Get Invoice Details by ID +```go +// the second argument is an ID, it should be valid +invoice, err := c.GetInvoiceDetails(ctx, "INV2-XFXV-YW42-ZANU-4F33") +``` +* for now, we are yet to implement the ShowAllInvoices endpoint, so use the following cURL request for the same(this gives you the list of invoice-IDs for this customer) + ```bash + curl -v -X GET https://api-m.sandbox.paypal.com/v2/invoicing/invoices?total_required=true \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " + ``` + +* refer to the beginning of this Usage section for obtaining a Token. + + ## How to Contribute * Fork a repository diff --git a/invoicing.go b/invoicing.go new file mode 100644 index 0000000..2143253 --- /dev/null +++ b/invoicing.go @@ -0,0 +1,38 @@ +package paypal + +import ( + "context" + "fmt" +) + +// GenerateInvoiceNumber: generates the next invoice number that is available to the merchant. +// Endpoint: POST /v2/invoicing/generate-next-invoice-number +func (c *Client) GenerateInvoiceNumber(ctx context.Context) (*InvoiceNumber, error) { + + req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/invoicing/generate-next-invoice-number"), nil) + nextInvoiceNumber := &InvoiceNumber{} + if err != nil { + return nextInvoiceNumber, err + } + + if err = c.SendWithAuth(req, nextInvoiceNumber); err != nil { + return nextInvoiceNumber, err + } + + return nextInvoiceNumber, nil +} + +// GetInvoiceDetails: show invoice details for a particular invoice by ID. +// Endpoint: GET /v2/invoicing/invoices/{invoice_id} +func (c *Client) GetInvoiceDetails(ctx context.Context, invoiceID string) (*Invoice, error) { + req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/invoicing/invoices/", invoiceID), nil) + invoice := &Invoice{} + if err != nil { + return invoice, err + } + + if err = c.SendWithAuth(req, invoice); err != nil { + return invoice, err + } + return invoice, nil +} diff --git a/invoicing_test.go b/invoicing_test.go new file mode 100644 index 0000000..34d2c66 --- /dev/null +++ b/invoicing_test.go @@ -0,0 +1,406 @@ +package paypal_test + +import ( + "context" + "encoding/json" + "testing" + + "github.com/plutov/paypal/v4" + "github.com/stretchr/testify/assert" +) + +// All test values are defined here +var devTestClientID = "AXy9orp-CDaHhBZ9C78QHW2BKZpACgroqo85_NIOa9mIfJ9QnSVKzY-X_rivR_fTUUr6aLjcJsj6sDur" +var devTestSecret = "EBoIiUSkCKeSk49hHSgTem1qnjzzJgRQHDEHvGpzlLEf_nIoJd91xu8rPOBDCdR_UYNKVxJE-UgS2iCw" +var devAPIBaseSandBox = "https://api.sandbox.paypal.com" + +func TestGenerateInvoiceNumber(t *testing.T) { + ctx := context.Background() + + c, _ := paypal.NewClient(devTestClientID, devTestSecret, devAPIBaseSandBox) + _, err := c.GetAccessToken(ctx) + assert.Equal(t, nil, err) + + _, err = c.GenerateInvoiceNumber(ctx) + assert.Equal(t, nil, err) +} +func assertTwoInvoices(t *testing.T, invoice paypal.Invoice, testInvoice paypal.Invoice) { + + // additional_recipients + assert.Equal(t, len(invoice.AdditionalRecipients), len(testInvoice.AdditionalRecipients)) + // additional_recipients --> email_address !! EQUALITY OF SPLICE OF STRUCT REMAINING !! + + // amount + assert.Equal(t, invoice.AmountSummary.Currency, testInvoice.AmountSummary.Currency) + assert.Equal(t, invoice.AmountSummary.Value, testInvoice.AmountSummary.Value) + // amount-->breakdown-->custom-->amount + assert.Equal(t, invoice.AmountSummary.Breakdown.Custom.Amount.Currency, testInvoice.AmountSummary.Breakdown.Custom.Amount.Currency) + assert.Equal(t, invoice.AmountSummary.Breakdown.Custom.Amount.Value, testInvoice.AmountSummary.Breakdown.Custom.Amount.Value) + // amount-->breakdown-->custom-->label + assert.Equal(t, invoice.AmountSummary.Breakdown.Custom.Label, testInvoice.AmountSummary.Breakdown.Custom.Label) + // amount-->breakdown-->discount-->amount + assert.Equal(t, invoice.AmountSummary.Breakdown.Discount.InvoiceDiscount.DiscountAmount.Currency, testInvoice.AmountSummary.Breakdown.Discount.InvoiceDiscount.DiscountAmount.Currency) + assert.Equal(t, invoice.AmountSummary.Breakdown.Discount.InvoiceDiscount.DiscountAmount.Value, testInvoice.AmountSummary.Breakdown.Discount.InvoiceDiscount.DiscountAmount.Value) + // amount-->breakdown-->discount-->percent + assert.Equal(t, invoice.AmountSummary.Breakdown.Discount.InvoiceDiscount.Percent, testInvoice.AmountSummary.Breakdown.Discount.InvoiceDiscount.Percent) + // amount-->breakdown-->discount-->item_discount + assert.Equal(t, invoice.AmountSummary.Breakdown.Discount.ItemDiscount.Currency, testInvoice.AmountSummary.Breakdown.Discount.ItemDiscount.Currency) + assert.Equal(t, invoice.AmountSummary.Breakdown.Discount.ItemDiscount.Value, testInvoice.AmountSummary.Breakdown.Discount.ItemDiscount.Value) + // amount-->breakdown-->item_total + assert.Equal(t, invoice.AmountSummary.Breakdown.ItemTotal.Currency, testInvoice.AmountSummary.Breakdown.ItemTotal.Currency) + assert.Equal(t, invoice.AmountSummary.Breakdown.ItemTotal.Value, testInvoice.AmountSummary.Breakdown.ItemTotal.Value) + // amount-->breakdown-->shipping-->amount + assert.Equal(t, invoice.AmountSummary.Breakdown.Shipping.Amount.Currency, testInvoice.AmountSummary.Breakdown.Shipping.Amount.Currency) + assert.Equal(t, invoice.AmountSummary.Breakdown.Shipping.Amount.Value, testInvoice.AmountSummary.Breakdown.Shipping.Amount.Value) + // amount-->breakdown-->shipping-->tax + assert.Equal(t, invoice.AmountSummary.Breakdown.Shipping.Tax.Amount.Currency, testInvoice.AmountSummary.Breakdown.Shipping.Tax.Amount.Currency) + assert.Equal(t, invoice.AmountSummary.Breakdown.Shipping.Tax.Amount.Value, testInvoice.AmountSummary.Breakdown.Shipping.Tax.Amount.Value) + assert.Equal(t, invoice.AmountSummary.Breakdown.Shipping.Tax.ID, testInvoice.AmountSummary.Breakdown.Shipping.Tax.ID) + assert.Equal(t, invoice.AmountSummary.Breakdown.Shipping.Tax.Name, testInvoice.AmountSummary.Breakdown.Shipping.Tax.Name) + assert.Equal(t, invoice.AmountSummary.Breakdown.Shipping.Tax.Percent, testInvoice.AmountSummary.Breakdown.Shipping.Tax.Percent) + // amount-->breakdown-->tax_total + assert.Equal(t, invoice.AmountSummary.Breakdown.TaxTotal.Currency, testInvoice.AmountSummary.Breakdown.TaxTotal.Currency) + assert.Equal(t, invoice.AmountSummary.Breakdown.TaxTotal.Value, testInvoice.AmountSummary.Breakdown.TaxTotal.Value) + + // configuration + assert.Equal(t, invoice.Configuration.AllowTip, testInvoice.Configuration.AllowTip) + assert.Equal(t, invoice.Configuration.TaxCalculatedAfterDiscount, testInvoice.Configuration.TaxCalculatedAfterDiscount) + assert.Equal(t, invoice.Configuration.TaxInclusive, testInvoice.Configuration.TaxInclusive) + assert.Equal(t, invoice.Configuration.TemplateId, testInvoice.Configuration.TemplateId) + // configuration --> partial_payment + assert.Equal(t, invoice.Configuration.PartialPayment.AllowPartialPayment, testInvoice.Configuration.PartialPayment.AllowPartialPayment) + assert.Equal(t, invoice.Configuration.PartialPayment.MinimumAmountDue.Currency, testInvoice.Configuration.PartialPayment.MinimumAmountDue.Currency) + assert.Equal(t, invoice.Configuration.PartialPayment.MinimumAmountDue.Value, testInvoice.Configuration.PartialPayment.MinimumAmountDue.Value) + + // detail + assert.Equal(t, invoice.Detail.CurrencyCode, testInvoice.Detail.CurrencyCode) + assert.Equal(t, invoice.Detail.InvoiceDate, testInvoice.Detail.InvoiceDate) + assert.Equal(t, invoice.Detail.InvoiceNumber, testInvoice.Detail.InvoiceNumber) + assert.Equal(t, invoice.Detail.Memo, testInvoice.Detail.Memo) + assert.Equal(t, invoice.Detail.Note, testInvoice.Detail.Note) + assert.Equal(t, invoice.Detail.Reference, testInvoice.Detail.Reference) + assert.Equal(t, invoice.Detail.TermsAndConditions, testInvoice.Detail.TermsAndConditions) + // detail --> attachments !! EQUALITY OF SPLICE OF STRUCT REMAINING !! + assert.Equal(t, len(invoice.Detail.Attachments), len(testInvoice.Detail.Attachments)) + // detail --> metadata + assert.Equal(t, invoice.Detail.Metadata.CancelTime, testInvoice.Detail.Metadata.CancelTime) + assert.Equal(t, invoice.Detail.Metadata.CancellledTimeBy, testInvoice.Detail.Metadata.CancellledTimeBy) + assert.Equal(t, invoice.Detail.Metadata.CreateTime, testInvoice.Detail.Metadata.CreateTime) + assert.Equal(t, invoice.Detail.Metadata.CreatedBy, testInvoice.Detail.Metadata.CreatedBy) + assert.Equal(t, invoice.Detail.Metadata.CreatedByFlow, testInvoice.Detail.Metadata.CreatedByFlow) + assert.Equal(t, invoice.Detail.Metadata.FirstSentTime, testInvoice.Detail.Metadata.FirstSentTime) + assert.Equal(t, invoice.Detail.Metadata.InvoicerViewUrl, testInvoice.Detail.Metadata.InvoicerViewUrl) + assert.Equal(t, invoice.Detail.Metadata.LastSentBy, testInvoice.Detail.Metadata.LastSentBy) + assert.Equal(t, invoice.Detail.Metadata.LastSentTime, testInvoice.Detail.Metadata.LastSentTime) + assert.Equal(t, invoice.Detail.Metadata.LastUpdateTime, testInvoice.Detail.Metadata.LastUpdateTime) + assert.Equal(t, invoice.Detail.Metadata.LastUpdatedBy, testInvoice.Detail.Metadata.LastUpdatedBy) + assert.Equal(t, invoice.Detail.Metadata.RecipientViewUrl, testInvoice.Detail.Metadata.RecipientViewUrl) + // detail --> payment_term + assert.Equal(t, invoice.Detail.PaymentTerm.DueDate, testInvoice.Detail.PaymentTerm.DueDate) + assert.Equal(t, invoice.Detail.PaymentTerm.TermType, testInvoice.Detail.PaymentTerm.TermType) + + // due_amount + assert.Equal(t, invoice.DueAmount.Currency, testInvoice.DueAmount.Currency) + assert.Equal(t, invoice.DueAmount.Value, testInvoice.DueAmount.Value) + + // gratuity + assert.Equal(t, invoice.Gratuity.Currency, testInvoice.Gratuity.Currency) + assert.Equal(t, invoice.Gratuity.Value, testInvoice.Gratuity.Value) + + // id + assert.Equal(t, invoice.ID, testInvoice.ID) + + // invoicer + assert.Equal(t, invoice.Invoicer.AdditionalNotes, testInvoice.Invoicer.AdditionalNotes) + assert.Equal(t, invoice.Invoicer.EmailAddress, testInvoice.Invoicer.EmailAddress) + assert.Equal(t, invoice.Invoicer.LogoUrl, testInvoice.Invoicer.LogoUrl) + assert.Equal(t, invoice.Invoicer.TaxId, testInvoice.Invoicer.TaxId) + assert.Equal(t, invoice.Invoicer.Website, testInvoice.Invoicer.Website) + // !!! SPLICE EQUALITY STILL REMAINING !!!!! + // invoicer --> phones + assert.Equal(t, len(invoice.Invoicer.Phones), len(testInvoice.Invoicer.Phones)) + + // items + // !!! SPLICE EQUALITY STILL REMAINING !!!!! + assert.Equal(t, len(invoice.Items), len(testInvoice.Items)) + + // links + // !!! SPLICE EQUALITY STILL REMAINING !!!!! + assert.Equal(t, len(invoice.Links), len(testInvoice.Links)) + + // parent_id + assert.Equal(t, invoice.ParentID, testInvoice.ParentID) + + // payments + assert.Equal(t, invoice.Payments.PaidAmount.Currency, testInvoice.Payments.PaidAmount.Currency) + assert.Equal(t, invoice.Payments.PaidAmount.Value, testInvoice.Payments.PaidAmount.Value) + // payments --> transactions + assert.Equal(t, len(invoice.Payments.Transactions), len(testInvoice.Payments.Transactions)) + + // primary_recipients + // !!! SPLICE EQUALITY STILL REMAINING !!!!! + assert.Equal(t, len(invoice.PrimaryRecipients), len(testInvoice.PrimaryRecipients)) + + // refunds + assert.Equal(t, invoice.Refunds.RefundAmount.Currency, testInvoice.Refunds.RefundAmount.Currency) + assert.Equal(t, invoice.Refunds.RefundAmount.Value, testInvoice.Refunds.RefundAmount.Value) + assert.Equal(t, len(invoice.Refunds.RefundDetails), len(testInvoice.Refunds.RefundDetails)) + + // status + assert.Equal(t, invoice.Status, testInvoice.Status) + +} + +func TestGetInvoice(t *testing.T) { + testInvoiceJSONData := []byte(` + { + "amount": { + "breakdown": { + "custom": { + "amount": { + "currency_code": "USD", + "value": "10.00" + }, + "label": "Packing Charges" + }, + "discount": { + "invoice_discount": { + "amount": { + "currency_code": "USD", + "value": "-2.63" + }, + "percent": "5" + }, + "item_discount": { + "currency_code": "USD", + "value": "-7.50" + } + }, + "item_total": { + "currency_code": "USD", + "value": "60.00" + }, + "shipping": { + "amount": { + "currency_code": "USD", + "value": "10.00" + }, + "tax": { + "amount": { + "currency_code": "USD", + "value": "0.73" + }, + "id": "TAX-9AU06895VD287170A", + "name": "Sales Tax", + "percent": "7.25" + } + }, + "tax_total": { + "currency_code": "USD", + "value": "4.34" + } + }, + "currency_code": "USD", + "value": "74.21" + }, + "configuration": { + "allow_tip": true, + "partial_payment": { + "allow_partial_payment": true, + "minimum_amount_due": { + "currency_code": "USD", + "value": "20.00" + } + }, + "tax_calculated_after_discount": true, + "tax_inclusive": false, + "template_id": "TEMP-4NW98229SC0703920" + }, + "detail": { + "additional_data": "2-4", + "archived": false, + "category_code": "SHIPPABLE", + "currency_code": "USD", + "group_draft": false, + "invoice_date": "2018-11-12", + "invoice_number": "0001", + "memo": "This is a long contract", + "metadata": { + "caller_type": "API_V2_INVOICE", + "create_time": "2022-10-25T16:54:50Z", + "created_by_flow": "REGULAR_SINGLE", + "invoicer_view_url": "https://www.sandbox.paypal.com/invoice/details/INV2-XFXV-YW42-ZANU-4F33", + "last_update_time": "2022-10-25T16:54:50Z", + "recipient_view_url": "https://www.sandbox.paypal.com/invoice/p/#XFXVYW42ZANU4F33" + }, + "note": "Thank you for your business.", + "payment_term": { + "due_date": "2018-11-22", + "term_type": "NET_10" + }, + "reference": "deal-ref", + "viewed_by_recipient": false + }, + "due_amount": { + "currency_code": "USD", + "value": "74.21" + }, + "id": "INV2-XFXV-YW42-ZANU-4F33", + "invoicer": { + "additional_notes": "2-4", + "address": { + "address_line_1": "1234 First Street", + "address_line_2": "337673 Hillside Court", + "admin_area_1": "CA", + "admin_area_2": "Anytown", + "country_code": "US", + "postal_code": "98765" + }, + "email_address": "merchant@example.com", + "logo_url": "https://example.com/logo.PNG", + "name": { + "full_name": "David Larusso", + "given_name": "David", + "surname": "Larusso" + }, + "phones": [ + { + "country_code": "001", + "national_number": "4085551234", + "phone_type": "MOBILE" + } + ], + "tax_id": "ABcNkWSfb5ICTt73nD3QON1fnnpgNKBy- Jb5SeuGj185MNNw6g", + "website": "www.test.com" + }, + "items": [ + { + "description": "Elastic mat to practice yoga.", + "discount": { + "amount": { + "currency_code": "USD", + "value": "-2.50" + }, + "percent": "5" + }, + "id": "ITEM-5335764681676603X", + "name": "Yoga Mat", + "quantity": "1", + "tax": { + "amount": { + "currency_code": "USD", + "value": "3.27" + }, + "id": "TAX-5XV24702TP4910056", + "name": "Sales Tax", + "percent": "7.25" + }, + "unit_amount": { + "currency_code": "USD", + "value": "50.00" + }, + "unit_of_measure": "QUANTITY" + }, + { + "discount": { + "amount": { + "currency_code": "USD", + "value": "-5.00" + } + }, + "id": "ITEM-1B467958Y9218273X", + "name": "Yoga t-shirt", + "quantity": "1", + "tax": { + "amount": { + "currency_code": "USD", + "value": "0.34" + }, + "id": "TAX-5XV24702TP4910056", + "name": "Sales Tax", + "percent": "7.25" + }, + "unit_amount": { + "currency_code": "USD", + "value": "10.00" + }, + "unit_of_measure": "QUANTITY" + } + ], + "links": [ + { + "href": "https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-XFXV-YW42-ZANU-4F33", + "method": "GET", + "rel": "self" + }, + { + "href": "https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-XFXV-YW42-ZANU-4F33/send", + "method": "POST", + "rel": "send" + }, + { + "href": "https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-XFXV-YW42-ZANU-4F33", + "method": "PUT", + "rel": "replace" + }, + { + "href": "https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-XFXV-YW42-ZANU-4F33", + "method": "DELETE", + "rel": "delete" + }, + { + "href": "https://api.sandbox.paypal.com/v2/invoicing/invoices/INV2-XFXV-YW42-ZANU-4F33/payments", + "method": "POST", + "rel": "record-payment" + } + ], + "primary_recipients": [ + { + "billing_info": { + "address": { + "address_line_1": "1234 Main Street", + "admin_area_1": "CA", + "admin_area_2": "Anytown", + "country_code": "US", + "postal_code": "98765" + }, + "email_address": "bill-me@example.com", + "name": { + "full_name": "Stephanie Meyers", + "given_name": "Stephanie", + "surname": "Meyers" + } + }, + "shipping_info": { + "address": { + "address_line_1": "1234 Main Street", + "admin_area_1": "CA", + "admin_area_2": "Anytown", + "country_code": "US", + "postal_code": "98765" + }, + "name": { + "full_name": "Stephanie Meyers", + "given_name": "Stephanie", + "surname": "Meyers" + } + } + } + ], + "status": "DRAFT", + "unilateral": false + } + `) + var testInvoice paypal.Invoice + err := json.Unmarshal(testInvoiceJSONData, &testInvoice) + assert.Equal(t, nil, err) // if passed, means unmarshalling was successful + + ctx := context.Background() + + c, _ := paypal.NewClient(devTestClientID, devTestSecret, devAPIBaseSandBox) + _, _ = c.GetAccessToken(ctx) + + invoice, err := c.GetInvoiceDetails(ctx, "INV2-XFXV-YW42-ZANU-4F33") + assert.Equal(t, nil, err) // if passed, means that request was successful + assertTwoInvoices(t, *invoice, testInvoice) +} diff --git a/types.go b/types.go index 3e5b0e4..bc8ddce 100644 --- a/types.go +++ b/types.go @@ -204,6 +204,264 @@ type ( CancelURL string `json:"cancel_url,omitempty"` } + // Invoicing relates structures + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#invoices_generate-next-invoice-number + InvoiceNumber struct { + InvoiceNumberValue string `json:"invoice_number"` + } + + // used in InvoiceAmountWithBreakdown + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-custom_amount + CustomAmount struct { + Label string `json:"label"` + Amount Money `json:"amount,omitempty"` + } + // Used in AggregatedDiscount + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-discount + InvoicingDiscount struct { + DiscountAmount Money `json:"amount,omitempty"` + Percent string `json:"percent,omitempty"` + } + // Used in InvoiceAmountWithBreakdown + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-aggregated_discount + AggregatedDiscount struct { + InvoiceDiscount InvoicingDiscount `json:"invoice_discount,omitempty"` + ItemDiscount *Money `json:"item_discount,omitempty"` + } + + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-tax + InvoiceTax struct { + Name string `json:"name,omitempty"` + Percent string `json:"percent,omitempty"` + ID string `json:"id,omitempty"` // not mentioned here, but is still returned in response payload, when invoice is requested by ID. + Amount Money `json:"amount,omitempty"` + } + // Used in InvoiceAmountWithBreakdown struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-shipping_cost + InvoiceShippingCost struct { + Amount Money `json:"amount,omitempty"` + Tax InvoiceTax `json:"tax,omitempty"` + } + + // Used in AmountSummaryDetail + // Doc: https://developer.paypal.com/docs/api/payments/v2/#definition-nrp-nrr_attributes + InvoiceAmountWithBreakdown struct { + Custom CustomAmount `json:"custom,omitempty"` // The custom amount to apply to an invoice. + Discount AggregatedDiscount `json:"discount,omitempty"` + ItemTotal Money `json:"item_total,omitempty"` // The subtotal for all items. + Shipping InvoiceShippingCost `json:"shipping,omitempty"` // The shipping fee for all items. Includes tax on shipping. + TaxTotal Money `json:"tax_total,omitempty"` + } + + // Invoice AmountSummary + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-amount_summary_detail + AmountSummaryDetail struct { + Breakdown InvoiceAmountWithBreakdown `json:"breakdown,omitempty"` + Currency string `json:"currency_code,omitempty"` + Value string `json:"value,omitempty"` + } + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-partial_payment + InvoicePartialPayment struct { + AllowPartialPayment bool `json:"allow_partial_payment,omitempty"` + MinimumAmountDue Money `json:"minimum_amount_due,omitempty"` // Valid only when allow_partial_payment is true. + } + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-configuration + InvoiceConfiguration struct { + AllowTip bool `json:"allow_tip,omitempty"` + PartialPayment InvoicePartialPayment `json:"partial_payment,omitempty"` + TaxCalculatedAfterDiscount bool `json:"tax_calculated_after_discount,omitempty"` + TaxInclusive bool `json:"tax_inclusive,omitempty"` + TemplateId string `json:"template_id,omitempty"` + } + // used in InvoiceDetail structure + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-file_reference + InvoiceFileReference struct { + ContentType string `json:"content_type,omitempty"` + CreateTime string `json:"create_time,omitempty"` + ID string `json:"id,omitempty"` + URL string `json:"reference_url,omitempty"` + Size string `json:"size,omitempty"` + } + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-metadata + InvoiceAuditMetadata struct { + CreateTime string `json:"create_time,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + LastUpdateTime string `json:"last_update_time,omitempty"` + LastUpdatedBy string `json:"last_updated_by,omitempty"` + CancelTime string `json:"cancel_time,omitempty"` + CancellledTimeBy string `json:"cancelled_by,omitempty"` + CreatedByFlow string `json:"created_by_flow,omitempty"` + FirstSentTime string `json:"first_sent_time,omitempty"` + InvoicerViewUrl string `json:"invoicer_view_url,omitempty"` + LastSentBy string `json:"last_sent_by,omitempty"` + LastSentTime string `json:"last_sent_time,omitempty"` + RecipientViewUrl string `json:"recipient_view_url,omitempty"` + } + // used in InvoiceDetail struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-invoice_payment_term + InvoicePaymentTerm struct { + TermType string `json:"term_type,omitempty"` + DueDate string `json:"due_date,omitempty"` + } + + // used in Invoice struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-invoice_detail + InvoiceDetail struct { + CurrencyCode string `json:"currency_code"` // required, hence omitempty not used + Attachments []InvoiceFileReference `json:"attachments,omitempty"` + Memo string `json:"memo,omitempty"` + Note string `json:"note,omitempty"` + Reference string `json:"reference,omitempty"` + TermsAndConditions string `json:"terms_and_conditions,omitempty"` + InvoiceDate string `json:"invoice_date,omitempty"` + InvoiceNumber string `json:"invoice_number,omitempty"` + Metadata InvoiceAuditMetadata `json:"metadata,omitempty"` // The audit metadata. + PaymentTerm InvoicePaymentTerm `json:"payment_term,omitempty"` // payment due date for the invoice. Value is either but not both term_type or due_date. + } + + // used in InvoicerInfo struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-phone_detail + InvoicerPhoneDetail struct { + CountryCode string `json:"country_code"` + NationalNumber string `json:"national_number"` + ExtensionNumber string `json:"extension_number,omitempty"` + PhoneType string `json:"phone_type,omitempty"` + } + + // used in Invoice struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-invoicer_info + InvoicerInfo struct { + AdditionalNotes string `json:"additional_notes,omitempty"` + EmailAddress string `json:"email_address,omitempty"` + LogoUrl string `json:"logo_url,omitempty"` + Phones []InvoicerPhoneDetail `json:"phones,omitempty"` + TaxId string `json:"tax_id,omitempty"` + Website string `json:"website,omitempty"` + } + // Used in Invoice struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-item + InvoiceItem struct { + Name string `json:"name"` + Quantity string `json:"quantity"` + UnitAmount Money `json:"unit_amount"` + Description string `json:"description,omitempty"` + InvoiceDiscount InvoicingDiscount `json:"discount,omitempty"` + ID string `json:"id,omitempty"` + ItemDate string `json:"item_date,omitempty"` + Tax InvoiceTax `json:"tax,omitempty"` + UnitOfMeasure string `json:"unit_of_measure,omitempty"` + } + + // used in InvoiceAddressPortable + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-address_details + InvoiceAddressDetails struct { + BuildingName string `json:"building_name,omitempty"` + DeliveryService string `json:"delivery_service,omitempty"` + StreetName string `json:"street_name,omitempty"` + StreetNumber string `json:"street_number,omitempty"` + StreetType string `json:"street_type,omitempty"` + SubBuilding string `json:"sub_building,omitempty"` + } + + // used in InvoiceContactInfo + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-address_portable + InvoiceAddressPortable struct { + CountryCode string `json:"country_code"` + AddressDetails InvoiceAddressDetails `json:"address_details,omitempty"` + AddressLine1 string `json:"address_line_1,omitempty"` + AddressLine2 string `json:"address_line_2,omitempty"` + AddressLine3 string `json:"address_line_3,omitempty"` + AdminArea1 string `json:"admin_area_1,omitempty"` + AdminArea2 string `json:"admin_area_2,omitempty"` + AdminArea3 string `json:"admin_area_3,omitempty"` + AdminArea4 string `json:"admin_area_4,omitempty"` + PostalCode string `json:"postal_code,omitempty"` + } + + // used in InvoicePaymentDetails + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-contact_information + InvoiceContactInfo struct { + BusinessName string `json:"business_name,omitempty"` + RecipientAddress InvoiceAddressPortable `json:"address,omitempty"` // address of the recipient. + RecipientName Name `json:"name,omitempty"` // The first and Last name of the recipient. + } + //used in InvoicePayments struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-payment_detail + InvoicePaymentDetails struct { + Method string `json:"method"` + Amount Money `json:"amount,omitempty"` + Note string `json:"note,omitempty"` + PaymentDate string `json:"payment_date,omitempty"` + PaymentID string `json:"payment_id,omitempty"` + ShippingInfo InvoiceContactInfo `json:"shipping_info,omitempty"` // The recipient's shipping information. + Type string `json:"type,omitempty"` + } + + // used in Invoice + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-payments + InvoicePayments struct { + PaidAmount Money `json:"paid_amount,omitempty"` + Transactions []InvoicePaymentDetails `json:"transactions,omitempty"` + } + + // used in InvoiceRecipientInfo + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-billing_info + InvoiceBillingInfo struct { + AdditionalInfo string `json:"additional_info,omitempty"` + EmailAddress string `json:"email_address,omitempty"` + Language string `json:"language,omitempty"` + Phones []InvoicerPhoneDetail `json:"phones,omitempty"` // invoice recipient's phone numbers. + } + // used in Invoice struct + // Doc: + InvoiceRecipientInfo struct { + BillingInfo InvoiceBillingInfo `json:"billing_info,omitempty"` // billing information for the invoice recipient. + ShippingInfo InvoiceContactInfo `json:"shipping_info,omitempty"` // recipient's shipping information. + } + + // used in InvoiceRefund struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-refund_detail + InvoiceRefundDetails struct { + Method string `json:"method"` + RefundAmount Money `json:"amount,omitempty"` + RefundDate string `json:"refund_date,omitempty"` + RefundID string `json:"refund_id,omitempty"` + RefundType string `json:"type,omitempty"` + } + + // used in Invoice struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-refunds + InvoiceRefund struct { + RefundAmount Money `json:"refund_amount,omitempty"` + RefundDetails []InvoiceRefundDetails `json:"transactions,omitempty"` + } + + // used in Invoice struct + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-email_address + InvoiceEmailAddress struct { + EmailAddress string `json:"email_address,omitempty"` + } + + // to contain Invoice related fields + // Doc: https://developer.paypal.com/docs/api/invoicing/v2/#invoices_get + Invoice struct { + AdditionalRecipients []InvoiceEmailAddress `json:"additional_recipients,omitempty"` // An array of one or more CC: emails to which notifications are sent. + AmountSummary AmountSummaryDetail `json:"amount,omitempty"` + Configuration InvoiceConfiguration `json:"configuration,omitempty"` + Detail InvoiceDetail `json:"detail,omitempty"` + DueAmount Money `json:"due_amount,omitempty"` // balance amount outstanding after payments. + Gratuity Money `json:"gratuity,omitempty"` // amount paid by the payer as gratuity to the invoicer. + ID string `json:"id,omitempty"` + Invoicer InvoicerInfo `json:"invoicer,omitempty"` + Items []InvoiceItem `json:"items,omitempty"` + Links []Link `json:"links,omitempty"` + ParentID string `json:"parent_id,omitempty"` + Payments InvoicePayments `json:"payments,omitempty"` + PrimaryRecipients []InvoiceRecipientInfo `json:"primary_recipients,omitempty"` + Refunds InvoiceRefund `json:"refunds,omitempty"` // List of refunds against this invoice. + Status string `json:"status,omitempty"` + } + // Doc: https://developer.paypal.com/api/orders/v2/#definition-payment_method PaymentMethod struct { PayeePreferred PayeePreferred `json:"payee_preferred,omitempty"`