forked from go-packages/paypal
Compare commits
100 Commits
revert-121
...
master
Author | SHA1 | Date | |
---|---|---|---|
5c4c0e1b7b | |||
63bbbdf1e1 | |||
283485068c | |||
fb37ee85f2 | |||
f840b7aac7 | |||
6217511bbd | |||
27ffa97190 | |||
|
0a5a40c12c | ||
|
c9c1a38641 | ||
|
9451befb2b | ||
|
daabe58f88 | ||
|
5052fd4286 | ||
|
3713084b64 | ||
|
bfdf8e6e97 | ||
|
6ebb0352bc | ||
|
e5db87f4a3 | ||
|
cd3344edd1 | ||
|
b8f2c8c573 | ||
|
5fddf59f71 | ||
|
26473c3630 | ||
|
ebf596676e | ||
|
4c16ffad0a | ||
|
1bb626d559 | ||
|
f0575ee562 | ||
|
defe3e09a4 | ||
|
efc9bbdee0 | ||
|
b7d3cfd093 | ||
|
d94b350a31 | ||
|
48f8ee15ce | ||
|
7dc1f997d6 | ||
|
c9ae5c4190 | ||
|
5c5393bd47 | ||
|
d381642422 | ||
|
ca6845e257 | ||
|
fc3ffe5b60 | ||
|
bf9103c70e | ||
|
bf0c207b52 | ||
|
28f2333770 | ||
|
d815f6d8d9 | ||
|
1a2c109908 | ||
|
50acd8fce1 | ||
|
64bfbc9b80 | ||
|
6fb262a2b0 | ||
|
7599a2d162 | ||
|
ef48df5471 | ||
|
5a5daa8596 | ||
|
cbabac29c8 | ||
|
e6a47fe5e7 | ||
|
59e3c9f3b0 | ||
|
326d69cc9e | ||
|
4ab23e5f78 | ||
|
06067823c6 | ||
|
d5cba40cd6 | ||
|
c2074af736 | ||
|
297b4fa2c6 | ||
|
8e5d5220ab | ||
|
2d088532de | ||
|
f195993596 | ||
|
d2210adf9c | ||
|
7f2eec9b56 | ||
|
392c0e9d69 | ||
|
a3977a8e74 | ||
|
021cc68201 | ||
|
bbd66c6089 | ||
|
41099f6b6e | ||
|
ed294ef317 | ||
|
d90cb75e47 | ||
|
62598e5880 | ||
|
52acc61786 | ||
|
1e23f8dd7d | ||
|
36281c1526 | ||
|
0a85944fee | ||
|
f694e414cd | ||
|
a3c2eaa0d4 | ||
|
a5cff3c18c | ||
|
df9918548b | ||
|
f807b7d046 | ||
|
3b835ea26a | ||
|
e83fd911e0 | ||
|
cc8b3cee69 | ||
|
0f9d1cca16 | ||
|
98cae62470 | ||
|
ef386ff32f | ||
|
476102bb76 | ||
|
13112c66e5 | ||
|
5508bfebaf | ||
|
ced90e676f | ||
|
abd51d2823 | ||
|
d1e5575c35 | ||
|
289b2669ef | ||
|
761c20a4ed | ||
|
18b47a0652 | ||
|
a0d03ecb0d | ||
|
905bf2eaf2 | ||
|
a678ccb4b1 | ||
|
e84c6d06c5 | ||
|
2e16e4fb10 | ||
|
0461b35d07 | ||
|
d355a65df0 | ||
|
b3eb2c69dd |
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#### What does this PR do?
|
||||||
|
#### Where should the reviewer start?
|
||||||
|
#### How should this be manually tested?
|
||||||
|
#### Any background context you want to provide?
|
50
.github/workflows/lint-test.yaml
vendored
Normal file
50
.github/workflows/lint-test.yaml
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
name: Lint and Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- features/*
|
||||||
|
- testing/*
|
||||||
|
- fix/*
|
||||||
|
- enhance/*
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- staging
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.23"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: go get .
|
||||||
|
- name: Install linters
|
||||||
|
run: |
|
||||||
|
go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||||
|
go install mvdan.cc/unparam@latest
|
||||||
|
- name: go vet
|
||||||
|
run: go vet ${{ inputs.path }}
|
||||||
|
- name: staticcheck
|
||||||
|
run: staticcheck ${{ inputs.path }}
|
||||||
|
- name: unparam
|
||||||
|
run: unparam ${{ inputs.path }}
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.23"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: go get .
|
||||||
|
- name: Run Tests
|
||||||
|
run: go test -v -race ./...
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
vendor/
|
|
@ -1,8 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.11
|
|
||||||
- 1.12
|
|
||||||
install:
|
|
||||||
- export PATH=$PATH:$HOME/gopath/bin
|
|
||||||
script:
|
|
||||||
- go test -v -race
|
|
10
CONTRIBUTING.md
Normal file
10
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
First off all, thank you for considering contributing to this project. It's people like you that make it such a great tool.
|
||||||
|
|
||||||
|
Keep an open mind! Improving documentation, bug triaging, or writing tutorials are all examples of helpful contributions that mean less work for you.
|
||||||
|
|
||||||
|
Some basic suggestions to get you started:
|
||||||
|
- Make sure the PR is up-to-date with the latest changes in the main branch.
|
||||||
|
- Make sure the PR passes all the tests.
|
||||||
|
- Make sure the PR passes the linter.
|
||||||
|
- Make sure the PR is well documented and formatted.
|
||||||
|
- Make sure the PR is well tested.
|
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019 Aliaksandr Pliutau
|
Copyright (c) 2024 Aliaksandr Pliutau
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
144
README.md
144
README.md
|
@ -1,74 +1,31 @@
|
||||||
[![Go Report Card](https://goreportcard.com/badge/plutov/paypal)](https://goreportcard.com/report/plutov/paypal)
|
[Docs](https://pkg.go.dev/github.com/plutov/paypal)
|
||||||
[![Build Status](https://travis-ci.org/plutov/paypal.svg?branch=master)](https://travis-ci.org/plutov/paypal)
|
|
||||||
[![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/plutov/paypal)
|
|
||||||
|
|
||||||
### Go client for PayPal REST API
|
<p>
|
||||||
|
<a href="https://github.com/plutov/paypal/releases"><img src="https://img.shields.io/github/release/plutov/paypal.svg" alt="Latest Release"></a>
|
||||||
|
<a href="https://pkg.go.dev/github.com/plutov/paypal?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
Currently supports **v2** only, if you want to use **v1**, use **v1.1.4** git tag.
|
# Go client for PayPal REST API
|
||||||
|
|
||||||
### Coverage
|
## Paypal REST API Docs
|
||||||
|
|
||||||
* POST /v1/oauth2/token
|
[Get started with PayPal REST APIs](https://developer.paypal.com/api/rest/)
|
||||||
* POST /v1/identity/openidconnect/tokenservice
|
|
||||||
* GET /v1/identity/openidconnect/userinfo/?schema=**SCHEMA**
|
|
||||||
* POST /v1/payments/payouts
|
|
||||||
* GET /v1/payments/payouts/**ID**
|
|
||||||
* GET /v1/payments/payouts-item/**ID**
|
|
||||||
* POST /v1/payments/payouts-item/**ID**/cancel
|
|
||||||
* GET /v1/payment-experience/web-profiles
|
|
||||||
* POST /v1/payment-experience/web-profiles
|
|
||||||
* GET /v1/payment-experience/web-profiles/**ID**
|
|
||||||
* PUT /v1/payment-experience/web-profiles/**ID**
|
|
||||||
* DELETE /v1/payment-experience/web-profiles/**ID**
|
|
||||||
* POST /v1/vault/credit-cards
|
|
||||||
* DELETE /v1/vault/credit-cards/**ID**
|
|
||||||
* PATCH /v1/vault/credit-cards/**ID**
|
|
||||||
* GET /v1/vault/credit-cards/**ID**
|
|
||||||
* GET /v1/vault/credit-cards
|
|
||||||
* GET /v2/payments/authorizations/**ID**
|
|
||||||
* POST /v2/payments/authorizations/**ID**/capture
|
|
||||||
* POST /v2/payments/authorizations/**ID**/void
|
|
||||||
* POST /v2/payments/authorizations/**ID**/reauthorize
|
|
||||||
* GET /v1/payments/sale/**ID**
|
|
||||||
* POST /v1/payments/sale/**ID**/refund
|
|
||||||
* GET /v2/payments/refund/**ID**
|
|
||||||
* POST /v2/checkout/orders
|
|
||||||
* GET /v2/checkout/orders/**ID**
|
|
||||||
* PATCH /v2/checkout/orders/**ID**
|
|
||||||
* POST /v2/checkout/orders/**ID**/authorize
|
|
||||||
* POST /v2/checkout/orders/**ID**/capture
|
|
||||||
* GET /v1/payments/billing-plans
|
|
||||||
* POST /v1/payments/billing-plans
|
|
||||||
* PATCH /v1/payments/billing-plans/***ID***
|
|
||||||
* POST /v1/payments/billing-agreements
|
|
||||||
* POST /v1/payments/billing-agreements/***TOKEN***/agreement-execute
|
|
||||||
* POST /v1/notifications/webhooks
|
|
||||||
* GET /v1/notifications/webhooks
|
|
||||||
* GET /v1/notifications/webhooks/**ID**
|
|
||||||
* PATCH /v1/notifications/webhooks/**ID**
|
|
||||||
* DELETE /v1/notifications/webhooks/**ID**
|
|
||||||
* POST /v1/notifications/verify-webhook-signature
|
|
||||||
* POST /v1/reporting/transactions
|
|
||||||
|
|
||||||
### Missing endpoints
|
## Missing endpoints
|
||||||
It is possible that some endpoints are missing in this SDK Client, but you can use built-in **paypal** functions to perform a request: **NewClient -> NewRequest -> SendWithAuth**
|
|
||||||
|
|
||||||
### New Client
|
It is possible that some endpoints are missing in this client, but you can use built-in `paypal` functions to perform a request: `NewClient -> NewRequest -> SendWithAuth`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/plutov/paypal"
|
import "github.com/plutov/paypal/v4"
|
||||||
|
|
||||||
// If using Go Modules
|
|
||||||
// import "github.com/plutov/paypal/v3"
|
|
||||||
|
|
||||||
// Create a client instance
|
// Create a client instance
|
||||||
c, err := paypal.NewClient("clientID", "secretID", paypal.APIBaseSandBox)
|
c, err := paypal.NewClient("clientID", "secretID", paypal.APIBaseSandBox)
|
||||||
c.SetLog(os.Stdout) // Set log to terminal stdout
|
c.SetLog(os.Stdout) // Set log to terminal stdout
|
||||||
|
|
||||||
accessToken, err := c.GetAccessToken()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get authorization by ID
|
## Get authorization by ID
|
||||||
|
|
||||||
```go
|
```go
|
||||||
auth, err := c.GetAuthorization("2DC87612EK520411B")
|
auth, err := c.GetAuthorization("2DC87612EK520411B")
|
||||||
|
@ -92,21 +49,6 @@ auth, err := c.VoidAuthorization(authID)
|
||||||
auth, err := c.ReauthorizeAuthorization(authID, &paypal.Amount{Total: "7.00", Currency: "USD"})
|
auth, err := c.ReauthorizeAuthorization(authID, &paypal.Amount{Total: "7.00", Currency: "USD"})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get Sale by ID
|
|
||||||
|
|
||||||
```go
|
|
||||||
sale, err := c.GetSale("36C38912MN9658832")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Refund Sale by ID
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Full
|
|
||||||
refund, err := c.RefundSale(saleID, nil)
|
|
||||||
// Partial
|
|
||||||
refund, err := c.RefundSale(saleID, &paypal.Amount{Total: "7.00", Currency: "USD"})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get Refund by ID
|
### Get Refund by ID
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -122,7 +64,11 @@ order, err := c.GetOrder("O-4J082351X3132253H")
|
||||||
### Create an Order
|
### Create an Order
|
||||||
|
|
||||||
```go
|
```go
|
||||||
order, err := c.CreateOrder(paypal.OrderIntentCapture, []paypal.PurchaseUnitRequest{paypal.PurchaseUnitRequest{ReferenceID: "ref-id", Amount: paypal.Amount{Total: "7.00", Currency: "USD"}}})
|
ctx := context.Background()
|
||||||
|
units := []paypal.PurchaseUnitRequest{}
|
||||||
|
source := &paypal.PaymentSource{}
|
||||||
|
appCtx := &paypal.ApplicationContext{}
|
||||||
|
order, err := c.CreateOrder(ctx, paypal.OrderIntentCapture, units, ource, appCtx)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Update Order by ID
|
### Update Order by ID
|
||||||
|
@ -178,7 +124,7 @@ payout := paypal.Payout{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
payoutResp, err := c.CreateSinglePayout(payout)
|
payoutResp, err := c.CreatePayout(payout)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get payout by ID
|
### Get payout by ID
|
||||||
|
@ -240,7 +186,6 @@ webprofiles, err := c.GetWebProfiles()
|
||||||
### Update web experience profile
|
### Update web experience profile
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
||||||
webprofile := WebProfile{
|
webprofile := WebProfile{
|
||||||
ID: "XP-CP6S-W9DY-96H8-MVN2",
|
ID: "XP-CP6S-W9DY-96H8-MVN2",
|
||||||
Name: "Shop YeowZa! YeowZa! ",
|
Name: "Shop YeowZa! YeowZa! ",
|
||||||
|
@ -288,6 +233,7 @@ c.GetCreditCards(nil)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Webhooks
|
### Webhooks
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Create a webhook
|
// Create a webhook
|
||||||
c.CreateWebhook(paypal.CreateWebhookRequest{
|
c.CreateWebhook(paypal.CreateWebhookRequest{
|
||||||
|
@ -295,7 +241,7 @@ c.CreateWebhook(paypal.CreateWebhookRequest{
|
||||||
EventTypes: []paypal.WebhookEventType{
|
EventTypes: []paypal.WebhookEventType{
|
||||||
paypal.WebhookEventType{
|
paypal.WebhookEventType{
|
||||||
Name: "PAYMENT.AUTHORIZATION.CREATED",
|
Name: "PAYMENT.AUTHORIZATION.CREATED",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -320,22 +266,46 @@ c.DeleteWebhook("WebhookID")
|
||||||
|
|
||||||
// List registered webhooks
|
// List registered webhooks
|
||||||
c.ListWebhooks(paypal.AncorTypeApplication)
|
c.ListWebhooks(paypal.AncorTypeApplication)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### How to Contribute
|
### Generate Next Invoice Number
|
||||||
|
|
||||||
* Fork a repository
|
```go
|
||||||
* Add/Fix something
|
// GenerateInvoiceNumber: generates the next invoice number that is available to the merchant.
|
||||||
* Check that tests are passing
|
c.GenerateInvoiceNumber(ctx) // might return something like "0001" or "0010".
|
||||||
* Create PR
|
```
|
||||||
|
|
||||||
Current contributors:
|
### 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 <Token>"
|
||||||
|
```
|
||||||
|
|
||||||
|
- refer to the beginning of this Usage section for obtaining a Token.
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
- Fork a repository
|
||||||
|
- Add/Fix something
|
||||||
|
- Check that tests are passing
|
||||||
|
- Create PR
|
||||||
|
|
||||||
|
Main contributors:
|
||||||
|
|
||||||
- [Roopak Venkatakrishnan](https://github.com/roopakv)
|
|
||||||
- [Alex Pliutau](https://github.com/plutov)
|
- [Alex Pliutau](https://github.com/plutov)
|
||||||
|
- [Roopak Venkatakrishnan](https://github.com/roopakv)
|
||||||
|
|
||||||
### Tests
|
## Tests
|
||||||
|
|
||||||
* Unit tests: `go test -v ./...`
|
```
|
||||||
* Integration tests: `go test -tags=integration`
|
go test -v ./...
|
||||||
|
```
|
||||||
|
|
|
@ -2,15 +2,16 @@ package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetAuthorization returns an authorization by ID
|
// GetAuthorization returns an authorization by ID
|
||||||
// Endpoint: GET /v2/payments/authorizations/ID
|
// Endpoint: GET /v2/payments/authorizations/ID
|
||||||
func (c *Client) GetAuthorization(authID string) (*Authorization, error) {
|
func (c *Client) GetAuthorization(ctx context.Context, authID string) (*Authorization, error) {
|
||||||
buf := bytes.NewBuffer([]byte(""))
|
buf := bytes.NewBuffer([]byte(""))
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/payments/authorizations/", authID), buf)
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/payments/authorizations/", authID), buf)
|
||||||
auth := &Authorization{}
|
auth := &Authorization{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -24,19 +25,19 @@ func (c *Client) GetAuthorization(authID string) (*Authorization, error) {
|
||||||
// CaptureAuthorization captures and process an existing authorization.
|
// CaptureAuthorization captures and process an existing authorization.
|
||||||
// To use this method, the original payment must have Intent set to "authorize"
|
// To use this method, the original payment must have Intent set to "authorize"
|
||||||
// Endpoint: POST /v2/payments/authorizations/ID/capture
|
// Endpoint: POST /v2/payments/authorizations/ID/capture
|
||||||
func (c *Client) CaptureAuthorization(authID string, paymentCaptureRequest *PaymentCaptureRequest) (*PaymentCaptureResponse, error) {
|
func (c *Client) CaptureAuthorization(ctx context.Context, authID string, paymentCaptureRequest *PaymentCaptureRequest) (*PaymentCaptureResponse, error) {
|
||||||
return c.CaptureAuthorizationWithPaypalRequestId(authID, paymentCaptureRequest, "")
|
return c.CaptureAuthorizationWithPaypalRequestId(ctx, authID, paymentCaptureRequest, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureAuthorization captures and process an existing authorization with idempotency.
|
// CaptureAuthorization captures and process an existing authorization with idempotency.
|
||||||
// To use this method, the original payment must have Intent set to "authorize"
|
// To use this method, the original payment must have Intent set to "authorize"
|
||||||
// Endpoint: POST /v2/payments/authorizations/ID/capture
|
// Endpoint: POST /v2/payments/authorizations/ID/capture
|
||||||
func (c *Client) CaptureAuthorizationWithPaypalRequestId(
|
func (c *Client) CaptureAuthorizationWithPaypalRequestId(ctx context.Context,
|
||||||
authID string,
|
authID string,
|
||||||
paymentCaptureRequest *PaymentCaptureRequest,
|
paymentCaptureRequest *PaymentCaptureRequest,
|
||||||
requestID string,
|
requestID string,
|
||||||
) (*PaymentCaptureResponse, error) {
|
) (*PaymentCaptureResponse, error) {
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/capture"), paymentCaptureRequest)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/capture"), paymentCaptureRequest)
|
||||||
paymentCaptureResponse := &PaymentCaptureResponse{}
|
paymentCaptureResponse := &PaymentCaptureResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -53,9 +54,9 @@ func (c *Client) CaptureAuthorizationWithPaypalRequestId(
|
||||||
|
|
||||||
// VoidAuthorization voids a previously authorized payment
|
// VoidAuthorization voids a previously authorized payment
|
||||||
// Endpoint: POST /v2/payments/authorizations/ID/void
|
// Endpoint: POST /v2/payments/authorizations/ID/void
|
||||||
func (c *Client) VoidAuthorization(authID string) (*Authorization, error) {
|
func (c *Client) VoidAuthorization(ctx context.Context, authID string) (*Authorization, error) {
|
||||||
buf := bytes.NewBuffer([]byte(""))
|
buf := bytes.NewBuffer([]byte(""))
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/void"), buf)
|
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/void"), buf)
|
||||||
auth := &Authorization{}
|
auth := &Authorization{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -67,11 +68,11 @@ func (c *Client) VoidAuthorization(authID string) (*Authorization, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReauthorizeAuthorization reauthorize a Paypal account payment.
|
// ReauthorizeAuthorization reauthorize a Paypal account payment.
|
||||||
// PayPal recommends to reauthorize payment after ~3 days
|
// PayPal recommends reauthorizing payment after ~3 days
|
||||||
// Endpoint: POST /v2/payments/authorizations/ID/reauthorize
|
// Endpoint: POST /v2/payments/authorizations/ID/reauthorize
|
||||||
func (c *Client) ReauthorizeAuthorization(authID string, a *Amount) (*Authorization, error) {
|
func (c *Client) ReauthorizeAuthorization(ctx context.Context, authID string, a *Amount) (*Authorization, error) {
|
||||||
buf := bytes.NewBuffer([]byte(`{"amount":{"currency":"` + a.Currency + `","total":"` + a.Total + `"}}`))
|
buf := bytes.NewBuffer([]byte(`{"amount":{"currency_code":"` + a.Currency + `","value":"` + a.Total + `"}}`))
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/reauthorize"), buf)
|
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/reauthorize"), buf)
|
||||||
auth := &Authorization{}
|
auth := &Authorization{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
133
billing.go
133
billing.go
|
@ -1,133 +0,0 @@
|
||||||
package paypal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// CreateBillingResp struct
|
|
||||||
CreateBillingResp struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
State string `json:"state,omitempty"`
|
|
||||||
PaymentDefinitions []PaymentDefinition `json:"payment_definitions,omitempty"`
|
|
||||||
MerchantPreferences MerchantPreferences `json:"merchant_preferences,omitempty"`
|
|
||||||
CreateTime time.Time `json:"create_time,omitempty"`
|
|
||||||
UpdateTime time.Time `json:"update_time,omitempty"`
|
|
||||||
Links []Link `json:"links,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateAgreementResp struct
|
|
||||||
CreateAgreementResp struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Description string `json:"description,omitempty"`
|
|
||||||
Plan BillingPlan `json:"plan,omitempty"`
|
|
||||||
Links []Link `json:"links,omitempty"`
|
|
||||||
StartTime time.Time `json:"start_time,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// BillingPlanListParams struct
|
|
||||||
BillingPlanListParams struct {
|
|
||||||
Page string `json:"page,omitempty"` //Default: 0.
|
|
||||||
Status string `json:"status,omitempty"` //Allowed values: CREATED, ACTIVE, INACTIVE, ALL.
|
|
||||||
PageSize string `json:"page_size,omitempty"` //Default: 10.
|
|
||||||
TotalRequired string `json:"total_required,omitempty"` //Default: no.
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//BillingPlanListResp struct
|
|
||||||
BillingPlanListResp struct {
|
|
||||||
Plans []BillingPlan `json:"plans,omitempty"`
|
|
||||||
TotalItems string `json:"total_items,omitempty"`
|
|
||||||
TotalPages string `json:"total_pages,omitempty"`
|
|
||||||
Links []Link `json:"links,omitempty"`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateBillingPlan creates a billing plan in Paypal
|
|
||||||
// Endpoint: POST /v1/payments/billing-plans
|
|
||||||
func (c *Client) CreateBillingPlan(plan BillingPlan) (*CreateBillingResp, error) {
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans"), plan)
|
|
||||||
response := &CreateBillingResp{}
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
err = c.SendWithAuth(req, response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActivatePlan activates a billing plan
|
|
||||||
// By default, a new plan is not activated
|
|
||||||
// Endpoint: PATCH /v1/payments/billing-plans/
|
|
||||||
func (c *Client) ActivatePlan(planID string) error {
|
|
||||||
buf := bytes.NewBuffer([]byte(`[{"op":"replace","path":"/","value":{"state":"ACTIVE"}}]`))
|
|
||||||
req, err := http.NewRequest("PATCH", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans/"+planID), buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req.SetBasicAuth(c.ClientID, c.Secret)
|
|
||||||
req.Header.Set("Authorization", "Bearer "+c.Token.Token)
|
|
||||||
return c.SendWithAuth(req, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateBillingAgreement creates an agreement for specified plan
|
|
||||||
// Endpoint: POST /v1/payments/billing-agreements
|
|
||||||
func (c *Client) CreateBillingAgreement(a BillingAgreement) (*CreateAgreementResp, error) {
|
|
||||||
// PayPal needs only ID, so we will remove all fields except Plan ID
|
|
||||||
a.Plan = BillingPlan{
|
|
||||||
ID: a.Plan.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-agreements"), a)
|
|
||||||
response := &CreateAgreementResp{}
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
err = c.SendWithAuth(req, response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteApprovedAgreement - Use this call to execute (complete) a PayPal agreement that has been approved by the payer.
|
|
||||||
// Endpoint: POST /v1/payments/billing-agreements/token/agreement-execute
|
|
||||||
func (c *Client) ExecuteApprovedAgreement(token string) (*ExecuteAgreementResponse, error) {
|
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-agreements/"+token+"/agreement-execute"), nil)
|
|
||||||
response := &ExecuteAgreementResponse{}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.SetBasicAuth(c.ClientID, c.Secret)
|
|
||||||
req.Header.Set("Authorization", "Bearer "+c.Token.Token)
|
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, response); err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.ID == "" {
|
|
||||||
return response, errors.New("Unable to execute agreement with token=" + token)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListBillingPlans lists billing-plans
|
|
||||||
// Endpoint: GET /v1/payments/billing-plans
|
|
||||||
func (c *Client) ListBillingPlans(bplp BillingPlanListParams) (*BillingPlanListResp, error) {
|
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans"), nil)
|
|
||||||
q := req.URL.Query()
|
|
||||||
q.Add("page", bplp.Page)
|
|
||||||
q.Add("page_size", bplp.PageSize)
|
|
||||||
q.Add("status", bplp.Status)
|
|
||||||
q.Add("total_required", bplp.TotalRequired)
|
|
||||||
req.URL.RawQuery = q.Encode()
|
|
||||||
response := &BillingPlanListResp{}
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
err = c.SendWithAuth(req, response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
82
client.go
82
client.go
|
@ -2,11 +2,11 @@ package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"time"
|
"time"
|
||||||
|
@ -30,9 +30,9 @@ func NewClient(clientID string, secret string, APIBase string) (*Client, error)
|
||||||
// GetAccessToken returns struct of TokenResponse
|
// GetAccessToken returns struct of TokenResponse
|
||||||
// No need to call SetAccessToken to apply new access token for current Client
|
// No need to call SetAccessToken to apply new access token for current Client
|
||||||
// Endpoint: POST /v1/oauth2/token
|
// Endpoint: POST /v1/oauth2/token
|
||||||
func (c *Client) GetAccessToken() (*TokenResponse, error) {
|
func (c *Client) GetAccessToken(ctx context.Context) (*TokenResponse, error) {
|
||||||
buf := bytes.NewBuffer([]byte("grant_type=client_credentials"))
|
buf := bytes.NewBuffer([]byte("grant_type=client_credentials"))
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/oauth2/token"), buf)
|
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/oauth2/token"), buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &TokenResponse{}, err
|
return &TokenResponse{}, err
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func (c *Client) SetReturnRepresentation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send makes a request to the API, the response body will be
|
// Send makes a request to the API, the response body will be
|
||||||
// unmarshaled into v, or if v is an io.Writer, the response will
|
// unmarshalled into v, or if v is an io.Writer, the response will
|
||||||
// be written to it without decoding
|
// be written to it without decoding
|
||||||
func (c *Client) Send(req *http.Request, v interface{}) error {
|
func (c *Client) Send(req *http.Request, v interface{}) error {
|
||||||
var (
|
var (
|
||||||
|
@ -97,21 +97,36 @@ func (c *Client) Send(req *http.Request, v interface{}) error {
|
||||||
if c.returnRepresentation {
|
if c.returnRepresentation {
|
||||||
req.Header.Set("Prefer", "return=representation")
|
req.Header.Set("Prefer", "return=representation")
|
||||||
}
|
}
|
||||||
|
if c.Log != nil {
|
||||||
|
if reqDump, err := httputil.DumpRequestOut(req, true); err == nil {
|
||||||
|
c.Log.Write([]byte(fmt.Sprintf("Request: %s\n", reqDump)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err = c.Client.Do(req)
|
resp, err = c.Client.Do(req)
|
||||||
c.log(req, resp)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
|
||||||
|
if c.Log != nil {
|
||||||
|
if respDump, err := httputil.DumpResponse(resp, true); err == nil {
|
||||||
|
c.Log.Write([]byte(fmt.Sprintf("Response from %s: %s\n", req.URL, respDump)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func(Body io.ReadCloser) error {
|
||||||
|
return Body.Close()
|
||||||
|
}(resp.Body)
|
||||||
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||||
errResp := &ErrorResponse{Response: resp}
|
errResp := &ErrorResponse{Response: resp}
|
||||||
data, err = ioutil.ReadAll(resp.Body)
|
data, err = io.ReadAll(resp.Body)
|
||||||
|
|
||||||
if err == nil && len(data) > 0 {
|
if err == nil && len(data) > 0 {
|
||||||
json.Unmarshal(data, errResp)
|
err := json.Unmarshal(data, errResp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errResp
|
return errResp
|
||||||
|
@ -121,8 +136,8 @@ func (c *Client) Send(req *http.Request, v interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if w, ok := v.(io.Writer); ok {
|
if w, ok := v.(io.Writer); ok {
|
||||||
io.Copy(w, resp.Body)
|
_, err := io.Copy(w, resp.Body)
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.NewDecoder(resp.Body).Decode(v)
|
return json.NewDecoder(resp.Body).Decode(v)
|
||||||
|
@ -133,25 +148,25 @@ func (c *Client) Send(req *http.Request, v interface{}) error {
|
||||||
// making the main request
|
// making the main request
|
||||||
// client.Token will be updated when changed
|
// client.Token will be updated when changed
|
||||||
func (c *Client) SendWithAuth(req *http.Request, v interface{}) error {
|
func (c *Client) SendWithAuth(req *http.Request, v interface{}) error {
|
||||||
c.Lock()
|
// c.Lock()
|
||||||
|
c.mu.Lock()
|
||||||
// Note: Here we do not want to `defer c.Unlock()` because we need `c.Send(...)`
|
// Note: Here we do not want to `defer c.Unlock()` because we need `c.Send(...)`
|
||||||
// to happen outside of the locked section.
|
// to happen outside of the locked section.
|
||||||
|
|
||||||
if c.Token != nil {
|
if c.Token == nil || (!c.tokenExpiresAt.IsZero() && time.Until(c.tokenExpiresAt) < RequestNewTokenBeforeExpiresIn) {
|
||||||
if !c.tokenExpiresAt.IsZero() && c.tokenExpiresAt.Sub(time.Now()) < RequestNewTokenBeforeExpiresIn {
|
// c.Token will be updated in GetAccessToken call
|
||||||
// c.Token will be updated in GetAccessToken call
|
if _, err := c.GetAccessToken(req.Context()); err != nil {
|
||||||
if _, err := c.GetAccessToken(); err != nil {
|
// c.Unlock()
|
||||||
c.Unlock()
|
c.mu.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Authorization", "Bearer "+c.Token.Token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Authorization", "Bearer "+c.Token.Token)
|
||||||
// Unlock the client mutex before sending the request, this allows multiple requests
|
// Unlock the client mutex before sending the request, this allows multiple requests
|
||||||
// to be in progress at the same time.
|
// to be in progress at the same time.
|
||||||
c.Unlock()
|
// c.Unlock()
|
||||||
|
c.mu.Unlock()
|
||||||
return c.Send(req, v)
|
return c.Send(req, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +179,7 @@ func (c *Client) SendWithBasicAuth(req *http.Request, v interface{}) error {
|
||||||
|
|
||||||
// NewRequest constructs a request
|
// NewRequest constructs a request
|
||||||
// Convert payload to a JSON
|
// Convert payload to a JSON
|
||||||
func (c *Client) NewRequest(method, url string, payload interface{}) (*http.Request, error) {
|
func (c *Client) NewRequest(ctx context.Context, method, url string, payload interface{}) (*http.Request, error) {
|
||||||
var buf io.Reader
|
var buf io.Reader
|
||||||
if payload != nil {
|
if payload != nil {
|
||||||
b, err := json.Marshal(&payload)
|
b, err := json.Marshal(&payload)
|
||||||
|
@ -173,24 +188,5 @@ func (c *Client) NewRequest(method, url string, payload interface{}) (*http.Requ
|
||||||
}
|
}
|
||||||
buf = bytes.NewBuffer(b)
|
buf = bytes.NewBuffer(b)
|
||||||
}
|
}
|
||||||
return http.NewRequest(method, url, buf)
|
return http.NewRequestWithContext(ctx, method, url, buf)
|
||||||
}
|
|
||||||
|
|
||||||
// log will dump request and response to the log file
|
|
||||||
func (c *Client) log(r *http.Request, resp *http.Response) {
|
|
||||||
if c.Log != nil {
|
|
||||||
var (
|
|
||||||
reqDump string
|
|
||||||
respDump []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
if r != nil {
|
|
||||||
reqDump = fmt.Sprintf("%s %s. Data: %s", r.Method, r.URL.String(), r.Form.Encode())
|
|
||||||
}
|
|
||||||
if resp != nil {
|
|
||||||
respDump, _ = httputil.DumpResponse(resp, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Log.Write([]byte(fmt.Sprintf("Request: %s\nResponse: %s\n", reqDump, string(respDump))))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
94
client_test.go
Normal file
94
client_test.go
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package paypal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const alphabet = "abcedfghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
var testClientID = "AXy9orp-CDaHhBZ9C78QHW2BKZpACgroqo85_NIOa9mIfJ9QnSVKzY-X_rivR_fTUUr6aLjcJsj6sDur"
|
||||||
|
var testSecret = "EBoIiUSkCKeSk49hHSgTem1qnjzzJgRQHDEHvGpzlLEf_nIoJd91xu8rPOBDCdR_UYNKVxJE-UgS2iCw"
|
||||||
|
|
||||||
|
func RandomString(n int) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
k := len(alphabet)
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
c := alphabet[rand.Intn(k)]
|
||||||
|
sb.WriteByte(c)
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRandomProduct(t *testing.T) Product {
|
||||||
|
//create a product
|
||||||
|
productData := Product{
|
||||||
|
Name: RandomString(10),
|
||||||
|
Description: RandomString(100),
|
||||||
|
Category: ProductCategorySoftware,
|
||||||
|
Type: ProductTypeService,
|
||||||
|
ImageUrl: "https://example.com/image.png",
|
||||||
|
HomeUrl: "https://example.com",
|
||||||
|
}
|
||||||
|
return productData
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a simple copy of the SendWithAuth method, used to
|
||||||
|
// test the Lock and Unlock methods of the private mutex field
|
||||||
|
// of Client structure.
|
||||||
|
func (c *Client) sendWithAuth(req *http.Request, v interface{}) error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if c.Token == nil || (!c.tokenExpiresAt.IsZero() && time.Until(c.tokenExpiresAt) < RequestNewTokenBeforeExpiresIn) {
|
||||||
|
if _, err := c.GetAccessToken(req.Context()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+c.Token.Token)
|
||||||
|
|
||||||
|
return c.Send(req, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this method is used to invoke the sendWithAuth method, which will then check
|
||||||
|
// operationally the privated mutex field of Client structure.
|
||||||
|
func (c *Client) createProduct(ctx context.Context, product Product) (*CreateProductResponse, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/catalogs/products"), product)
|
||||||
|
response := &CreateProductResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
err = c.sendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientMutex(t *testing.T) {
|
||||||
|
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
||||||
|
c.GetAccessToken(context.Background())
|
||||||
|
|
||||||
|
// Operational testing of the private mutex field
|
||||||
|
n_iter := 2
|
||||||
|
|
||||||
|
errs := make(chan error)
|
||||||
|
|
||||||
|
for i := 0; i < n_iter; i++ {
|
||||||
|
go func() {
|
||||||
|
_, err := c.createProduct(context.Background(), createRandomProduct(t))
|
||||||
|
errs <- err
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n_iter; i++ {
|
||||||
|
err := <-errs
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
118
const.go
Normal file
118
const.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package paypal
|
||||||
|
|
||||||
|
type SubscriptionPlanStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SubscriptionPlanStatusCreated SubscriptionPlanStatus = "CREATED"
|
||||||
|
SubscriptionPlanStatusInactive SubscriptionPlanStatus = "INACTIVE"
|
||||||
|
SubscriptionPlanStatusActive SubscriptionPlanStatus = "ACTIVE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BillingPlanStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
BillingPlanStatusActive BillingPlanStatus = "ACTIVE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IntervalUnit string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IntervalUnitDay IntervalUnit = "DAY"
|
||||||
|
IntervalUnitWeek IntervalUnit = "WEEK"
|
||||||
|
IntervalUnitMonth IntervalUnit = "MONTH"
|
||||||
|
IntervalUnitYear IntervalUnit = "YEAR"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TenureType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TenureTypeRegular TenureType = "REGULAR"
|
||||||
|
TenureTypeTrial TenureType = "TRIAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SetupFeeFailureAction string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SetupFeeFailureActionContinue SetupFeeFailureAction = "CONTINUE"
|
||||||
|
SetupFeeFailureActionCancel SetupFeeFailureAction = "CANCEL"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShippingPreference string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ShippingPreferenceGetFromFile ShippingPreference = "GET_FROM_FILE"
|
||||||
|
ShippingPreferenceNoShipping ShippingPreference = "NO_SHIPPING"
|
||||||
|
ShippingPreferenceSetProvidedAddress ShippingPreference = "SET_PROVIDED_ADDRESS"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserAction string
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserActionContinue UserAction = "CONTINUE"
|
||||||
|
UserActionPayNow UserAction = "PAY_NOW"
|
||||||
|
UserActionSubscribeNow UserAction = "SUBSCRIBE_NOW"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubscriptionStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SubscriptionStatusApprovalPending SubscriptionStatus = "APPROVAL_PENDING"
|
||||||
|
SubscriptionStatusApproved SubscriptionStatus = "APPROVED"
|
||||||
|
SubscriptionStatusActive SubscriptionStatus = "ACTIVE"
|
||||||
|
SubscriptionStatusSuspended SubscriptionStatus = "SUSPENDED"
|
||||||
|
SubscriptionStatusCancelled SubscriptionStatus = "CANCELLED"
|
||||||
|
SubscriptionStatusExpired SubscriptionStatus = "EXPIRED"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-transaction
|
||||||
|
type SubscriptionTransactionStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SubscriptionCaptureStatusCompleted SubscriptionTransactionStatus = "COMPLETED"
|
||||||
|
SubscriptionCaptureStatusDeclined SubscriptionTransactionStatus = "DECLINED"
|
||||||
|
SubscriptionCaptureStatusPartiallyRefunded SubscriptionTransactionStatus = "PARTIALLY_REFUNDED"
|
||||||
|
SubscriptionCaptureStatusPending SubscriptionTransactionStatus = "PENDING"
|
||||||
|
SubscriptionCaptureStatusRefunded SubscriptionTransactionStatus = "REFUNDED"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CaptureType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CaptureTypeOutstandingBalance CaptureType = "OUTSTANDING_BALANCE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProductType string
|
||||||
|
type ProductCategory string //Doc: https://developer.paypal.com/docs/api/catalog-products/v1/#definition-product_category
|
||||||
|
const (
|
||||||
|
ProductTypePhysical ProductType = "PHYSICAL"
|
||||||
|
ProductTypeDigital ProductType = "DIGITAL"
|
||||||
|
ProductTypeService ProductType = "SERVICE"
|
||||||
|
|
||||||
|
ProductCategorySoftware ProductCategory = "SOFTWARE"
|
||||||
|
ProductCategorySoftwareComputerAndDataProcessingServices ProductCategory = "COMPUTER_AND_DATA_PROCESSING_SERVICES"
|
||||||
|
ProductCategorySoftwareDigitalGames ProductCategory = "DIGITAL_GAMES"
|
||||||
|
ProductCategorySoftwareGameSoftware ProductCategory = "GAME_SOFTWARE"
|
||||||
|
ProductCategorySoftwareGames ProductCategory = "GAMES"
|
||||||
|
ProductCategorySoftwareGeneral ProductCategory = "GENERAL"
|
||||||
|
ProductCategorySoftwareGraphicAndCommercialDesign ProductCategory = "GRAPHIC_AND_COMMERCIAL_DESIGN"
|
||||||
|
ProductCategorySoftwareOemSoftware ProductCategory = "OEM_SOFTWARE"
|
||||||
|
ProductCategorySoftwareOnlineGaming ProductCategory = "ONLINE_GAMING"
|
||||||
|
ProductCategorySoftwareOnlineGamingCurrency ProductCategory = "ONLINE_GAMING_CURRENCY"
|
||||||
|
ProductCategorySoftwareOnlineServices ProductCategory = "ONLINE_SERVICES"
|
||||||
|
ProductCategorySoftwareOther ProductCategory = "OTHER"
|
||||||
|
ProductCategorySoftwareServices ProductCategory = "SERVICES"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PayeePreferred string // Doc: https://developer.paypal.com/api/orders/v2/#definition-payment_method
|
||||||
|
const (
|
||||||
|
PayeePreferredUnrestricted PayeePreferred = "UNRESTRICTED"
|
||||||
|
PayeePreferredImmediatePaymentRequired PayeePreferred = "IMMEDIATE_PAYMENT_REQUIRED"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StandardEntryClassCode string // Doc: https://developer.paypal.com/api/orders/v2/#definition-payment_method
|
||||||
|
const (
|
||||||
|
StandardEntryClassCodeTel StandardEntryClassCode="TEL"
|
||||||
|
StandardEntryClassCodeWeb StandardEntryClassCode="WEB"
|
||||||
|
StandardEntryClassCodeCcd StandardEntryClassCode="CCD"
|
||||||
|
StandardEntryClassCodePpd StandardEntryClassCode="PPD"
|
||||||
|
)
|
|
@ -1,17 +0,0 @@
|
||||||
package paypal_test
|
|
||||||
|
|
||||||
import "github.com/plutov/paypal/v3"
|
|
||||||
|
|
||||||
func Example() {
|
|
||||||
// Initialize client
|
|
||||||
c, err := paypal.NewClient("clientID", "secretID", paypal.APIBaseSandBox)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve access token
|
|
||||||
_, err = c.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
12
go.mod
12
go.mod
|
@ -1,3 +1,11 @@
|
||||||
module github.com/plutov/paypal/v3
|
module euphoria-laxis.fr/go-packages/paypale/v4
|
||||||
|
|
||||||
go 1.12
|
go 1.23
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.9.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -0,0 +1,10 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
54
identity.go
54
identity.go
|
@ -1,65 +1,17 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GrantNewAccessTokenFromAuthCode - Use this call to grant a new access token, using the previously obtained authorization code.
|
|
||||||
// Endpoint: POST /v1/identity/openidconnect/tokenservice
|
|
||||||
func (c *Client) GrantNewAccessTokenFromAuthCode(code, redirectURI string) (*TokenResponse, error) {
|
|
||||||
token := &TokenResponse{}
|
|
||||||
|
|
||||||
q := url.Values{}
|
|
||||||
q.Set("grant_type", "authorization_code")
|
|
||||||
q.Set("code", code)
|
|
||||||
q.Set("redirect_uri", redirectURI)
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/identity/openidconnect/tokenservice"), strings.NewReader(q.Encode()))
|
|
||||||
if err != nil {
|
|
||||||
return token, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
|
|
||||||
if err = c.SendWithBasicAuth(req, token); err != nil {
|
|
||||||
return token, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GrantNewAccessTokenFromRefreshToken - Use this call to grant a new access token, using a refresh token.
|
|
||||||
// Endpoint: POST /v1/identity/openidconnect/tokenservice
|
|
||||||
func (c *Client) GrantNewAccessTokenFromRefreshToken(refreshToken string) (*TokenResponse, error) {
|
|
||||||
type request struct {
|
|
||||||
GrantType string `json:"grant_type"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
token := &TokenResponse{}
|
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/identity/openidconnect/tokenservice"), request{GrantType: "refresh_token", RefreshToken: refreshToken})
|
|
||||||
if err != nil {
|
|
||||||
return token, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, token); err != nil {
|
|
||||||
return token, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserInfo - Use this call to retrieve user profile attributes.
|
// GetUserInfo - Use this call to retrieve user profile attributes.
|
||||||
// Endpoint: GET /v1/identity/openidconnect/userinfo/?schema=<Schema>
|
// Endpoint: GET /v1/identity/openidconnect/userinfo/?schema=<Schema>
|
||||||
// Pass the schema that is used to return as per openidconnect protocol. The only supported schema value is openid.
|
func (c *Client) GetUserInfo(ctx context.Context, schema string) (*UserInfo, error) {
|
||||||
func (c *Client) GetUserInfo(schema string) (*UserInfo, error) {
|
|
||||||
u := &UserInfo{}
|
u := &UserInfo{}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v1/identity/openidconnect/userinfo/?schema=", schema), nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v1/identity/openidconnect/userinfo/?schema=", schema), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return u, err
|
return u, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
// +build integration
|
|
||||||
|
|
||||||
package paypal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// All test values are defined here
|
|
||||||
var testClientID = "AXy9orp-CDaHhBZ9C78QHW2BKZpACgroqo85_NIOa9mIfJ9QnSVKzY-X_rivR_fTUUr6aLjcJsj6sDur"
|
|
||||||
var testSecret = "EBoIiUSkCKeSk49hHSgTem1qnjzzJgRQHDEHvGpzlLEf_nIoJd91xu8rPOBDCdR_UYNKVxJE-UgS2iCw"
|
|
||||||
var testUserID = "https://www.paypal.com/webapps/auth/identity/user/VBqgHcgZwb1PBs69ybjjXfIW86_Hr93aBvF_Rgbh2II"
|
|
||||||
var testCardID = "CARD-54E6956910402550WKGRL6EA"
|
|
||||||
|
|
||||||
func TestGetAccessToken(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
token, err := c.GetAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Not expected error for GetAccessToken(), got %s", err.Error())
|
|
||||||
}
|
|
||||||
if token.Token == "" {
|
|
||||||
t.Errorf("Expected non-empty token for GetAccessToken()")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetUserInfo(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
u, err := c.GetUserInfo("openid")
|
|
||||||
if u.ID != testUserID || err != nil {
|
|
||||||
t.Errorf("GetUserInfo must return valid test ID %s, got %s, error: %v", testUserID, u.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateSinglePayout(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
payout := Payout{
|
|
||||||
SenderBatchHeader: &SenderBatchHeader{
|
|
||||||
SenderBatchID: "Payouts_2018_100007",
|
|
||||||
EmailSubject: "You have a payout!",
|
|
||||||
EmailMessage: "You have received a payout! Thanks for using our service!",
|
|
||||||
},
|
|
||||||
Items: []PayoutItem{
|
|
||||||
{
|
|
||||||
RecipientType: "EMAIL",
|
|
||||||
Receiver: "receiver@example.com",
|
|
||||||
Amount: &AmountPayout{
|
|
||||||
Value: "9.87",
|
|
||||||
Currency: "USD",
|
|
||||||
},
|
|
||||||
Note: "Thanks for your patronage!",
|
|
||||||
SenderItemID: "201403140001",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
c.CreateSinglePayout(payout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStoreCreditCard(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
r1, e1 := c.StoreCreditCard(CreditCard{})
|
|
||||||
if e1 == nil || r1 != nil {
|
|
||||||
t.Errorf("Error is expected for invalid CC")
|
|
||||||
}
|
|
||||||
|
|
||||||
r2, e2 := c.StoreCreditCard(CreditCard{
|
|
||||||
Number: "4417119669820331",
|
|
||||||
Type: "visa",
|
|
||||||
ExpireMonth: "11",
|
|
||||||
ExpireYear: "2020",
|
|
||||||
CVV2: "874",
|
|
||||||
FirstName: "Foo",
|
|
||||||
LastName: "Bar",
|
|
||||||
})
|
|
||||||
if e2 != nil || r2 == nil {
|
|
||||||
t.Errorf("200 code expected for valid CC card. Error: %v", e2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteCreditCard(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
e1 := c.DeleteCreditCard("")
|
|
||||||
if e1 == nil {
|
|
||||||
t.Errorf("Error is expected for invalid CC ID")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetCreditCard(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
r1, e1 := c.GetCreditCard("BBGGG")
|
|
||||||
if e1 == nil || r1 != nil {
|
|
||||||
t.Errorf("Error is expected for invalid CC, got CC %v", r1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetCreditCards(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
r1, e1 := c.GetCreditCards(nil)
|
|
||||||
if e1 != nil || r1 == nil {
|
|
||||||
t.Errorf("200 code expected. Error: %v", e1)
|
|
||||||
}
|
|
||||||
|
|
||||||
r2, e2 := c.GetCreditCards(&CreditCardsFilter{
|
|
||||||
Page: 2,
|
|
||||||
PageSize: 7,
|
|
||||||
})
|
|
||||||
if e2 != nil || r2 == nil {
|
|
||||||
t.Errorf("200 code expected. Error: %v", e2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPatchCreditCard(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
r1, e1 := c.PatchCreditCard(testCardID, nil)
|
|
||||||
if e1 == nil || r1 != nil {
|
|
||||||
t.Errorf("Error is expected for empty update info")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates, gets, and deletes single webhook
|
|
||||||
func TestCreateAndGetWebhook(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
payload := &CreateWebhookRequest{
|
|
||||||
URL: "https://example.com/paypal_webhooks",
|
|
||||||
EventTypes: []WebhookEventType{
|
|
||||||
WebhookEventType{
|
|
||||||
Name: "PAYMENT.AUTHORIZATION.CREATED",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
createdWebhook, err := c.CreateWebhook(payload)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Webhook couldn't be created, error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = c.GetWebhook(createdWebhook.ID)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("An error occurred while getting webhook, error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.DeleteWebhook(createdWebhook.ID)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("An error occurred while webhooks deletion, error %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates, updates, and deletes single webhook
|
|
||||||
func TestCreateAndUpdateWebhook(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
creationPayload := &CreateWebhookRequest{
|
|
||||||
URL: "https://example.com/paypal_webhooks",
|
|
||||||
EventTypes: []WebhookEventType{
|
|
||||||
WebhookEventType{
|
|
||||||
Name: "PAYMENT.AUTHORIZATION.CREATED",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
createdWebhook, err := c.CreateWebhook(creationPayload)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Webhook couldn't be created, error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePayload := []WebhookField{
|
|
||||||
WebhookField{
|
|
||||||
Operation: "replace",
|
|
||||||
Path: "/event_types",
|
|
||||||
Value: []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"name": "PAYMENT.SALE.REFUNDED",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = c.UpdateWebhook(createdWebhook.ID, updatePayload)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Couldn't update webhook, error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.DeleteWebhook(createdWebhook.ID)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("An error occurred while webhooks deletion, error %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListWebhooks(t *testing.T) {
|
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
|
||||||
c.GetAccessToken()
|
|
||||||
|
|
||||||
_, err := c.ListWebhooks(AncorTypeApplication)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Cannot registered list webhooks, error %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
38
invoicing.go
Normal file
38
invoicing.go
Normal file
|
@ -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
|
||||||
|
}
|
406
invoicing_test.go
Normal file
406
invoicing_test.go
Normal file
|
@ -0,0 +1,406 @@
|
||||||
|
package paypal_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"euphoria-laxis.fr/go-packages/paypale/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-5XV24702TP4910056",
|
||||||
|
"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)
|
||||||
|
}
|
108
order.go
108
order.go
|
@ -1,13 +1,17 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// GetOrder retrieves order by ID
|
// GetOrder retrieves order by ID
|
||||||
// Endpoint: GET /v2/checkout/orders/ID
|
// Endpoint: GET /v2/checkout/orders/ID
|
||||||
func (c *Client) GetOrder(orderID string) (*Order, error) {
|
func (c *Client) GetOrder(ctx context.Context, orderID string) (*Order, error) {
|
||||||
order := &Order{}
|
order := &Order{}
|
||||||
|
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/checkout/orders/", orderID), nil)
|
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/checkout/orders/", orderID), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return order, err
|
return order, err
|
||||||
}
|
}
|
||||||
|
@ -19,23 +23,39 @@ func (c *Client) GetOrder(orderID string) (*Order, error) {
|
||||||
return order, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOrder - Use this call to create an order
|
// Create an order
|
||||||
// Endpoint: POST /v2/checkout/orders
|
// Endpoint: POST /v2/checkout/orders
|
||||||
func (c *Client) CreateOrder(intent string, purchaseUnits []PurchaseUnitRequest, payer *CreateOrderPayer, appContext *ApplicationContext) (*Order, error) {
|
func (c *Client) CreateOrder(ctx context.Context, intent string, purchaseUnits []PurchaseUnitRequest, paymentSource *PaymentSource, appContext *ApplicationContext) (*Order, error) {
|
||||||
|
return c.CreateOrderWithPaypalRequestID(ctx, intent, purchaseUnits, paymentSource, appContext, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrderWithPaypalRequestID - Use this call to create an order with idempotency
|
||||||
|
// Endpoint: POST /v2/checkout/orders
|
||||||
|
func (c *Client) CreateOrderWithPaypalRequestID(ctx context.Context,
|
||||||
|
intent string,
|
||||||
|
purchaseUnits []PurchaseUnitRequest,
|
||||||
|
paymentSource *PaymentSource,
|
||||||
|
appContext *ApplicationContext,
|
||||||
|
requestID string,
|
||||||
|
) (*Order, error) {
|
||||||
type createOrderRequest struct {
|
type createOrderRequest struct {
|
||||||
Intent string `json:"intent"`
|
Intent string `json:"intent"`
|
||||||
Payer *CreateOrderPayer `json:"payer,omitempty"`
|
PaymentSource *PaymentSource `json:"payment_source,omitempty"`
|
||||||
PurchaseUnits []PurchaseUnitRequest `json:"purchase_units"`
|
PurchaseUnits []PurchaseUnitRequest `json:"purchase_units"`
|
||||||
ApplicationContext *ApplicationContext `json:"application_context,omitempty"`
|
ApplicationContext *ApplicationContext `json:"application_context,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
order := &Order{}
|
order := &Order{}
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders"), createOrderRequest{Intent: intent, PurchaseUnits: purchaseUnits, Payer: payer, ApplicationContext: appContext})
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders"), createOrderRequest{Intent: intent, PurchaseUnits: purchaseUnits, PaymentSource: paymentSource, ApplicationContext: appContext})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return order, err
|
return order, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if requestID != "" {
|
||||||
|
req.Header.Set("PayPal-Request-Id", requestID)
|
||||||
|
}
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, order); err != nil {
|
if err = c.SendWithAuth(req, order); err != nil {
|
||||||
return order, err
|
return order, err
|
||||||
}
|
}
|
||||||
|
@ -45,27 +65,37 @@ func (c *Client) CreateOrder(intent string, purchaseUnits []PurchaseUnitRequest,
|
||||||
|
|
||||||
// UpdateOrder updates the order by ID
|
// UpdateOrder updates the order by ID
|
||||||
// Endpoint: PATCH /v2/checkout/orders/ID
|
// Endpoint: PATCH /v2/checkout/orders/ID
|
||||||
func (c *Client) UpdateOrder(orderID string, purchaseUnits []PurchaseUnitRequest) (*Order, error) {
|
func (c *Client) UpdateOrder(ctx context.Context, orderID string, op string, path string, value map[string]string) error {
|
||||||
order := &Order{}
|
|
||||||
|
|
||||||
req, err := c.NewRequest("PATCH", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/checkout/orders/", orderID), purchaseUnits)
|
type patchRequest struct {
|
||||||
|
Op string `json:"op"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Value map[string]string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := c.NewRequest(ctx, "PATCH", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/checkout/orders/", orderID), []patchRequest{
|
||||||
|
{
|
||||||
|
Op: op,
|
||||||
|
Path: path,
|
||||||
|
Value: value,
|
||||||
|
},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return order, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, order); err != nil {
|
if err = c.SendWithAuth(req, nil); err != nil {
|
||||||
return order, err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return order, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizeOrder - https://developer.paypal.com/docs/api/orders/v2/#orders_authorize
|
// AuthorizeOrder - https://developer.paypal.com/docs/api/orders/v2/#orders_authorize
|
||||||
// Endpoint: POST /v2/checkout/orders/ID/authorize
|
// Endpoint: POST /v2/checkout/orders/ID/authorize
|
||||||
func (c *Client) AuthorizeOrder(orderID string, authorizeOrderRequest AuthorizeOrderRequest) (*Authorization, error) {
|
func (c *Client) AuthorizeOrder(ctx context.Context, orderID string, authorizeOrderRequest AuthorizeOrderRequest) (*AuthorizeOrderResponse, error) {
|
||||||
auth := &Authorization{}
|
auth := &AuthorizeOrderResponse{}
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders/"+orderID+"/authorize"), authorizeOrderRequest)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders/"+orderID+"/authorize"), authorizeOrderRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return auth, err
|
return auth, err
|
||||||
}
|
}
|
||||||
|
@ -79,22 +109,23 @@ func (c *Client) AuthorizeOrder(orderID string, authorizeOrderRequest AuthorizeO
|
||||||
|
|
||||||
// CaptureOrder - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
// CaptureOrder - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
||||||
// Endpoint: POST /v2/checkout/orders/ID/capture
|
// Endpoint: POST /v2/checkout/orders/ID/capture
|
||||||
func (c *Client) CaptureOrder(orderID string, captureOrderRequest CaptureOrderRequest) (*CaptureOrderResponse, error) {
|
func (c *Client) CaptureOrder(ctx context.Context, orderID string, captureOrderRequest CaptureOrderRequest) (*CaptureOrderResponse, error) {
|
||||||
return c.CaptureOrderWithPaypalRequestId(orderID, captureOrderRequest, "")
|
return c.CaptureOrderWithPaypalRequestId(ctx, orderID, captureOrderRequest, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureOrder with idempotency - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
// CaptureOrder with idempotency - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
||||||
// Endpoint: POST /v2/checkout/orders/ID/capture
|
// Endpoint: POST /v2/checkout/orders/ID/capture
|
||||||
// https://developer.paypal.com/docs/api/reference/api-requests/#http-request-headers
|
// https://developer.paypal.com/docs/api/reference/api-requests/#http-request-headers
|
||||||
func (c *Client) CaptureOrderWithPaypalRequestId(
|
func (c *Client) CaptureOrderWithPaypalRequestId(ctx context.Context,
|
||||||
orderID string,
|
orderID string,
|
||||||
captureOrderRequest CaptureOrderRequest,
|
captureOrderRequest CaptureOrderRequest,
|
||||||
requestID string,
|
requestID string,
|
||||||
|
mockResponse *CaptureOrderMockResponse,
|
||||||
) (*CaptureOrderResponse, error) {
|
) (*CaptureOrderResponse, error) {
|
||||||
capture := &CaptureOrderResponse{}
|
capture := &CaptureOrderResponse{}
|
||||||
|
|
||||||
c.SetReturnRepresentation()
|
c.SetReturnRepresentation()
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders/"+orderID+"/capture"), captureOrderRequest)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders/"+orderID+"/capture"), captureOrderRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return capture, err
|
return capture, err
|
||||||
}
|
}
|
||||||
|
@ -103,6 +134,15 @@ func (c *Client) CaptureOrderWithPaypalRequestId(
|
||||||
req.Header.Set("PayPal-Request-Id", requestID)
|
req.Header.Set("PayPal-Request-Id", requestID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mockResponse != nil {
|
||||||
|
mock, err := json.Marshal(mockResponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("PayPal-Mock-Response", string(mock))
|
||||||
|
}
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, capture); err != nil {
|
if err = c.SendWithAuth(req, capture); err != nil {
|
||||||
return capture, err
|
return capture, err
|
||||||
}
|
}
|
||||||
|
@ -112,20 +152,20 @@ func (c *Client) CaptureOrderWithPaypalRequestId(
|
||||||
|
|
||||||
// RefundCapture - https://developer.paypal.com/docs/api/payments/v2/#captures_refund
|
// RefundCapture - https://developer.paypal.com/docs/api/payments/v2/#captures_refund
|
||||||
// Endpoint: POST /v2/payments/captures/ID/refund
|
// Endpoint: POST /v2/payments/captures/ID/refund
|
||||||
func (c *Client) RefundCapture(captureID string, refundCaptureRequest RefundCaptureRequest) (*RefundResponse, error) {
|
func (c *Client) RefundCapture(ctx context.Context, captureID string, refundCaptureRequest RefundCaptureRequest) (*RefundResponse, error) {
|
||||||
return c.RefundCaptureWithPaypalRequestId(captureID, refundCaptureRequest, "")
|
return c.RefundCaptureWithPaypalRequestId(ctx, captureID, refundCaptureRequest, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefundCapture with idempotency - https://developer.paypal.com/docs/api/payments/v2/#captures_refund
|
// RefundCapture with idempotency - https://developer.paypal.com/docs/api/payments/v2/#captures_refund
|
||||||
// Endpoint: POST /v2/payments/captures/ID/refund
|
// Endpoint: POST /v2/payments/captures/ID/refund
|
||||||
func (c *Client) RefundCaptureWithPaypalRequestId(
|
func (c *Client) RefundCaptureWithPaypalRequestId(ctx context.Context,
|
||||||
captureID string,
|
captureID string,
|
||||||
refundCaptureRequest RefundCaptureRequest,
|
refundCaptureRequest RefundCaptureRequest,
|
||||||
requestID string,
|
requestID string,
|
||||||
) (*RefundResponse, error) {
|
) (*RefundResponse, error) {
|
||||||
refund := &RefundResponse{}
|
refund := &RefundResponse{}
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/captures/"+captureID+"/refund"), refundCaptureRequest)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/captures/"+captureID+"/refund"), refundCaptureRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return refund, err
|
return refund, err
|
||||||
}
|
}
|
||||||
|
@ -139,3 +179,19 @@ func (c *Client) RefundCaptureWithPaypalRequestId(
|
||||||
}
|
}
|
||||||
return refund, nil
|
return refund, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CapturedDetail - https://developer.paypal.com/docs/api/payments/v2/#captures_get
|
||||||
|
// Endpoint: GET /v2/payments/captures/ID
|
||||||
|
func (c *Client) CapturedDetail(ctx context.Context, captureID string) (*CaptureDetailsResponse, error) {
|
||||||
|
response := &CaptureDetailsResponse{}
|
||||||
|
|
||||||
|
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/captures/"+captureID), nil)
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.SendWithAuth(req, response); err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
19
payout.go
19
payout.go
|
@ -1,14 +1,15 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateSinglePayout submits a payout with an asynchronous API call, which immediately returns the results of a PayPal payment.
|
// CreatePayout submits a payout with an asynchronous API call, which immediately returns the results of a PayPal payment.
|
||||||
// For email payout set RecipientType: "EMAIL" and receiver email into Receiver
|
// For email payout set RecipientType: "EMAIL" and receiver email into Receiver
|
||||||
// Endpoint: POST /v1/payments/payouts
|
// Endpoint: POST /v1/payments/payouts
|
||||||
func (c *Client) CreateSinglePayout(p Payout) (*PayoutResponse, error) {
|
func (c *Client) CreatePayout(ctx context.Context, p Payout) (*PayoutResponse, error) {
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts"), p)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts"), p)
|
||||||
response := &PayoutResponse{}
|
response := &PayoutResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -25,8 +26,8 @@ func (c *Client) CreateSinglePayout(p Payout) (*PayoutResponse, error) {
|
||||||
// GetPayout shows the latest status of a batch payout along with the transaction status and other data for individual items.
|
// GetPayout shows the latest status of a batch payout along with the transaction status and other data for individual items.
|
||||||
// Also, returns IDs for the individual payout items. You can use these item IDs in other calls.
|
// Also, returns IDs for the individual payout items. You can use these item IDs in other calls.
|
||||||
// Endpoint: GET /v1/payments/payouts/ID
|
// Endpoint: GET /v1/payments/payouts/ID
|
||||||
func (c *Client) GetPayout(payoutBatchID string) (*PayoutResponse, error) {
|
func (c *Client) GetPayout(ctx context.Context, payoutBatchID string) (*PayoutResponse, error) {
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts/"+payoutBatchID), nil)
|
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts/"+payoutBatchID), nil)
|
||||||
response := &PayoutResponse{}
|
response := &PayoutResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -43,8 +44,8 @@ func (c *Client) GetPayout(payoutBatchID string) (*PayoutResponse, error) {
|
||||||
// GetPayoutItem shows the details for a payout item.
|
// GetPayoutItem shows the details for a payout item.
|
||||||
// Use this call to review the current status of a previously unclaimed, or pending, payout item.
|
// Use this call to review the current status of a previously unclaimed, or pending, payout item.
|
||||||
// Endpoint: GET /v1/payments/payouts-item/ID
|
// Endpoint: GET /v1/payments/payouts-item/ID
|
||||||
func (c *Client) GetPayoutItem(payoutItemID string) (*PayoutItemResponse, error) {
|
func (c *Client) GetPayoutItem(ctx context.Context, payoutItemID string) (*PayoutItemResponse, error) {
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID), nil)
|
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID), nil)
|
||||||
response := &PayoutItemResponse{}
|
response := &PayoutItemResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,8 +62,8 @@ func (c *Client) GetPayoutItem(payoutItemID string) (*PayoutItemResponse, error)
|
||||||
// CancelPayoutItem cancels an unclaimed Payout Item. If no one claims the unclaimed item within 30 days,
|
// CancelPayoutItem cancels an unclaimed Payout Item. If no one claims the unclaimed item within 30 days,
|
||||||
// the funds are automatically returned to the sender. Use this call to cancel the unclaimed item before the automatic 30-day refund.
|
// the funds are automatically returned to the sender. Use this call to cancel the unclaimed item before the automatic 30-day refund.
|
||||||
// Endpoint: POST /v1/payments/payouts-item/ID/cancel
|
// Endpoint: POST /v1/payments/payouts-item/ID/cancel
|
||||||
func (c *Client) CancelPayoutItem(payoutItemID string) (*PayoutItemResponse, error) {
|
func (c *Client) CancelPayoutItem(ctx context.Context, payoutItemID string) (*PayoutItemResponse, error) {
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID+"/cancel"), nil)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID+"/cancel"), nil)
|
||||||
response := &PayoutItemResponse{}
|
response := &PayoutItemResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
119
products.go
Normal file
119
products.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package paypal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Product struct
|
||||||
|
Product struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Category ProductCategory `json:"category,omitempty"`
|
||||||
|
Type ProductType `json:"type"`
|
||||||
|
ImageUrl string `json:"image_url,omitempty"`
|
||||||
|
HomeUrl string `json:"home_url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateProductResponse struct {
|
||||||
|
Product
|
||||||
|
SharedResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
ListProductsResponse struct {
|
||||||
|
Products []Product `json:"products"`
|
||||||
|
SharedListResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductListParameters struct {
|
||||||
|
ListParams
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Product) GetUpdatePatch() []Patch {
|
||||||
|
return []Patch{
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/description",
|
||||||
|
Value: p.Description,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/category",
|
||||||
|
Value: p.Category,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/image_url",
|
||||||
|
Value: p.ImageUrl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/home_url",
|
||||||
|
Value: p.HomeUrl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProduct creates a product
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/catalog-products/v1/#products_create
|
||||||
|
// Endpoint: POST /v1/catalogs/products
|
||||||
|
func (c *Client) CreateProduct(ctx context.Context, product Product) (*CreateProductResponse, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/catalogs/products"), product)
|
||||||
|
response := &CreateProductResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProduct. updates a product information
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/catalog-products/v1/#products_patch
|
||||||
|
// Endpoint: PATCH /v1/catalogs/products/:product_id
|
||||||
|
func (c *Client) UpdateProduct(ctx context.Context, product Product) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/catalogs/products/", product.ID), product.GetUpdatePatch())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get product details
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/catalog-products/v1/#products_get
|
||||||
|
// Endpoint: GET /v1/catalogs/products/:product_id
|
||||||
|
func (c *Client) GetProduct(ctx context.Context, productId string) (*Product, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/catalogs/products/", productId), nil)
|
||||||
|
response := &Product{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all products
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/catalog-products/v1/#products_list
|
||||||
|
// Endpoint: GET /v1/catalogs/products
|
||||||
|
func (c *Client) ListProducts(ctx context.Context, params *ProductListParameters) (*ListProductsResponse, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/catalogs/products"), nil)
|
||||||
|
response := &ListProductsResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if params != nil {
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("page", params.Page)
|
||||||
|
q.Add("page_size", params.PageSize)
|
||||||
|
q.Add("total_required", params.TotalRequired)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
45
sale.go
45
sale.go
|
@ -1,50 +1,17 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
// GetSale returns a sale by ID
|
"fmt"
|
||||||
// Use this call to get details about a sale transaction.
|
)
|
||||||
// Note: This call returns only the sales that were created via the REST API.
|
|
||||||
// Endpoint: GET /v1/payments/sale/ID
|
|
||||||
func (c *Client) GetSale(saleID string) (*Sale, error) {
|
|
||||||
sale := &Sale{}
|
|
||||||
|
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/sale/"+saleID), nil)
|
|
||||||
if err != nil {
|
|
||||||
return sale, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, sale); err != nil {
|
|
||||||
return sale, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sale, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RefundSale refunds a completed payment.
|
|
||||||
// Use this call to refund a completed payment. Provide the sale_id in the URI and an empty JSON payload for a full refund. For partial refunds, you can include an amount.
|
|
||||||
func (c *Client) RefundSale(saleID string, r RefundRequest) (*Refund, error) {
|
|
||||||
refund := &Refund{}
|
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/sale/"+saleID+"/refund"), &r)
|
|
||||||
if err != nil {
|
|
||||||
return refund, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, refund); err != nil {
|
|
||||||
return refund, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return refund, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRefund by ID
|
// GetRefund by ID
|
||||||
// Use it to look up details of a specific refund on direct and captured payments.
|
// Use it to look up details of a specific refund on direct and captured payments.
|
||||||
// Endpoint: GET /v2/payments/refund/ID
|
// Endpoint: GET /v2/payments/refund/ID
|
||||||
func (c *Client) GetRefund(refundID string) (*Refund, error) {
|
func (c *Client) GetRefund(ctx context.Context, refundID string) (*Refund, error) {
|
||||||
refund := &Refund{}
|
refund := &Refund{}
|
||||||
|
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/refund/"+refundID), nil)
|
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/refund/"+refundID), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return refund, err
|
return refund, err
|
||||||
}
|
}
|
||||||
|
|
229
subscription.go
229
subscription.go
|
@ -1,33 +1,125 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
SubscriptionBase struct {
|
||||||
|
PlanID string `json:"plan_id"`
|
||||||
|
StartTime *JSONTime `json:"start_time,omitempty"`
|
||||||
|
Quantity string `json:"quantity,omitempty"`
|
||||||
|
ShippingAmount *Money `json:"shipping_amount,omitempty"`
|
||||||
|
Subscriber *Subscriber `json:"subscriber,omitempty"`
|
||||||
|
AutoRenewal bool `json:"auto_renewal,omitempty"`
|
||||||
|
ApplicationContext *ApplicationContext `json:"application_context,omitempty"`
|
||||||
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
|
Plan *PlanOverride `json:"plan,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscriptionDetails struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
SubscriptionStatus SubscriptionStatus `json:"status,omitempty"`
|
||||||
|
SubscriptionStatusChangeNote string `json:"status_change_note,omitempty"`
|
||||||
|
StatusUpdateTime time.Time `json:"status_update_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Subscription struct {
|
||||||
|
SubscriptionDetailResp
|
||||||
|
}
|
||||||
|
|
||||||
// SubscriptionDetailResp struct
|
// SubscriptionDetailResp struct
|
||||||
SubscriptionDetailResp struct {
|
SubscriptionDetailResp struct {
|
||||||
ID string `json:"id,omitempty"`
|
SubscriptionBase
|
||||||
PlanID string `json:"plan_id,omitempty"`
|
SubscriptionDetails
|
||||||
StartTime time.Time `json:"start_time,omitempty"`
|
BillingInfo BillingInfo `json:"billing_info,omitempty"` // not found in documentation
|
||||||
Quantity string `json:"quantity,omitempty"`
|
SharedResponse
|
||||||
ShippingAmount ShippingAmount `json:"shipping_amount,omitempty"`
|
}
|
||||||
Subscriber Subscriber `json:"subscriber,omitempty"`
|
|
||||||
BillingInfo BillingInfo `json:"billing_info,omitempty"`
|
SubscriptionCaptureResponse struct {
|
||||||
CreateTime time.Time `json:"create_time,omitempty"`
|
Status SubscriptionTransactionStatus `json:"status"`
|
||||||
UpdateTime time.Time `json:"update_time,omitempty"`
|
Id string `json:"id"`
|
||||||
Links []Link `json:"links,omitempty"`
|
AmountWithBreakdown AmountWithBreakdown `json:"amount_with_breakdown"`
|
||||||
Status string `json:"status,omitempty"`
|
PayerName Name `json:"payer_name"`
|
||||||
StatusUpdateTime time.Time `json:"status_update_time,omitempty"`
|
PayerEmail string `json:"payer_email"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-amount_with_breakdown
|
||||||
|
AmountWithBreakdown struct {
|
||||||
|
GrossAmount Money `json:"gross_amount"`
|
||||||
|
FeeAmount Money `json:"fee_amount"`
|
||||||
|
ShippingAmount Money `json:"shipping_amount"`
|
||||||
|
TaxAmount Money `json:"tax_amount"`
|
||||||
|
NetAmount Money `json:"net_amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscriptionTransactionsParams struct {
|
||||||
|
SubscriptionId string
|
||||||
|
StartTime time.Time
|
||||||
|
EndTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscriptionTransactionsResponse struct {
|
||||||
|
Transactions []SubscriptionCaptureResponse `json:"transactions"`
|
||||||
|
SharedListResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptureRequest struct {
|
||||||
|
Note string `json:"note"`
|
||||||
|
CaptureType CaptureType `json:"capture_type"`
|
||||||
|
Amount Money `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-plan_override
|
||||||
|
PlanOverride struct {
|
||||||
|
BillingCycles []BillingCycleOverride `json:"billing_cycles,omitempty"`
|
||||||
|
PaymentPreferences *PaymentPreferencesOverride `json:"payment_preferences,omitempty"`
|
||||||
|
Taxes *TaxesOverride `json:"taxes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-payment_preferences_override
|
||||||
|
PaymentPreferencesOverride struct {
|
||||||
|
AutoBillOutstanding bool `json:"auto_bill_outstanding,omitempty"`
|
||||||
|
SetupFee Money `json:"setup_fee,omitempty"`
|
||||||
|
SetupFeeFailureAction SetupFeeFailureAction `json:"setup_fee_failure_action,omitempty"`
|
||||||
|
PaymentFailureThreshold int `json:"payment_failure_threshold,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-payment_preferences_override
|
||||||
|
TaxesOverride struct {
|
||||||
|
Percentage string `json:"percentage,omitempty"`
|
||||||
|
Inclusive *bool `json:"inclusive,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-billing_cycle_override
|
||||||
|
BillingCycleOverride struct {
|
||||||
|
PricingScheme PricingScheme `json:"pricing_scheme,omitempty"`
|
||||||
|
Sequence *int `json:"sequence,omitempty"`
|
||||||
|
TotalCycles *int `json:"total_cycles,omitempty"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetSubscriptionDetails shows details for a subscription, by ID.
|
func (p *Subscription) GetUpdatePatch() []Patch {
|
||||||
// Endpoint: GET /v1/billing/subscriptions/
|
result := []Patch{
|
||||||
func (c *Client) GetSubscriptionDetails(subscriptionID string) (*SubscriptionDetailResp, error) {
|
{
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
Operation: "replace",
|
||||||
|
Path: "/billing_info/outstanding_balance",
|
||||||
|
Value: p.BillingInfo.OutstandingBalance,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSubscriptionPlan creates a subscriptionPlan
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create
|
||||||
|
// Endpoint: POST /v1/billing/subscriptions
|
||||||
|
func (c *Client) CreateSubscription(ctx context.Context, newSubscription SubscriptionBase) (*SubscriptionDetailResp, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/billing/subscriptions"), newSubscription)
|
||||||
|
req.Header.Add("Prefer", "return=representation")
|
||||||
response := &SubscriptionDetailResp{}
|
response := &SubscriptionDetailResp{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
|
@ -35,3 +127,108 @@ func (c *Client) GetSubscriptionDetails(subscriptionID string) (*SubscriptionDet
|
||||||
err = c.SendWithAuth(req, response)
|
err = c.SendWithAuth(req, response)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateSubscriptionPlan. updates a plan
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_patch
|
||||||
|
// Endpoint: PATCH /v1/billing/subscriptions/:subscription_id
|
||||||
|
func (c *Client) UpdateSubscription(ctx context.Context, updatedSubscription Subscription) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/billing/subscriptions/", updatedSubscription.ID), updatedSubscription.GetUpdatePatch())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSubscriptionDetails shows details for a subscription, by ID.
|
||||||
|
// Endpoint: GET /v1/billing/subscriptions/
|
||||||
|
func (c *Client) GetSubscriptionDetails(ctx context.Context, subscriptionID string) (*SubscriptionDetailResp, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
||||||
|
response := &SubscriptionDetailResp{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activates the subscription.
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_activate
|
||||||
|
// Endpoint: POST /v1/billing/subscriptions/{id}/activate
|
||||||
|
func (c *Client) ActivateSubscription(ctx context.Context, subscriptionId, activateReason string) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/activate", c.APIBase, subscriptionId), map[string]string{"reason": activateReason})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancels the subscription.
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_cancel
|
||||||
|
// Endpoint: POST /v1/billing/subscriptions/{id}/cancel
|
||||||
|
func (c *Client) CancelSubscription(ctx context.Context, subscriptionId, cancelReason string) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/cancel", c.APIBase, subscriptionId), map[string]string{"reason": cancelReason})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captures an authorized payment from the subscriber on the subscription.
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_capture
|
||||||
|
// Endpoint: POST /v1/billing/subscriptions/{id}/capture
|
||||||
|
func (c *Client) CaptureSubscription(ctx context.Context, subscriptionId string, request CaptureRequest) (*SubscriptionCaptureResponse, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/capture", c.APIBase, subscriptionId), request)
|
||||||
|
response := &SubscriptionCaptureResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suspends the subscription.
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_suspend
|
||||||
|
// Endpoint: POST /v1/billing/subscriptions/{id}/suspend
|
||||||
|
func (c *Client) SuspendSubscription(ctx context.Context, subscriptionId, reason string) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/suspend", c.APIBase, subscriptionId), map[string]string{"reason": reason})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lists transactions for a subscription.
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_transactions
|
||||||
|
// Endpoint: GET /v1/billing/subscriptions/{id}/transactions
|
||||||
|
func (c *Client) GetSubscriptionTransactions(ctx context.Context, requestParams SubscriptionTransactionsParams) (*SubscriptionTransactionsResponse, error) {
|
||||||
|
startTime := requestParams.StartTime.Format("2006-01-02T15:04:05Z")
|
||||||
|
endTime := requestParams.EndTime.Format("2006-01-02T15:04:05Z")
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/v1/billing/subscriptions/%s/transactions?start_time=%s&end_time=%s", c.APIBase, requestParams.SubscriptionId, startTime, endTime), nil)
|
||||||
|
response := &SubscriptionTransactionsResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revise plan or quantity of subscription
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_revise
|
||||||
|
// Endpoint: POST /v1/billing/subscriptions/{id}/revise
|
||||||
|
func (c *Client) ReviseSubscription(ctx context.Context, subscriptionId string, reviseSubscription SubscriptionBase) (*SubscriptionDetailResp, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/revise", c.APIBase, subscriptionId), reviseSubscription)
|
||||||
|
response := &SubscriptionDetailResp{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
233
subscription_plan.go
Normal file
233
subscription_plan.go
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
package paypal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// SubscriptionDetailResp struct
|
||||||
|
SubscriptionPlan struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
ProductId string `json:"product_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status SubscriptionPlanStatus `json:"status"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
BillingCycles []BillingCycle `json:"billing_cycles"`
|
||||||
|
PaymentPreferences *PaymentPreferences `json:"payment_preferences"`
|
||||||
|
Taxes *Taxes `json:"taxes"`
|
||||||
|
QuantitySupported bool `json:"quantity_supported"` //Indicates whether you can subscribe to this plan by providing a quantity for the goods or service.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doc https://developer.paypal.com/docs/api/subscriptions/v1/#definition-billing_cycle
|
||||||
|
BillingCycle struct {
|
||||||
|
PricingScheme PricingScheme `json:"pricing_scheme"` // The active pricing scheme for this billing cycle. A free trial billing cycle does not require a pricing scheme.
|
||||||
|
Frequency Frequency `json:"frequency"` // The frequency details for this billing cycle.
|
||||||
|
TenureType TenureType `json:"tenure_type"` // The tenure type of the billing cycle. In case of a plan having trial cycle, only 2 trial cycles are allowed per plan. The possible values are:
|
||||||
|
Sequence int `json:"sequence"` // The order in which this cycle is to run among other billing cycles. For example, a trial billing cycle has a sequence of 1 while a regular billing cycle has a sequence of 2, so that trial cycle runs before the regular cycle.
|
||||||
|
TotalCycles int `json:"total_cycles"` // The number of times this billing cycle gets executed. Trial billing cycles can only be executed a finite number of times (value between 1 and 999 for total_cycles). Regular billing cycles can be executed infinite times (value of 0 for total_cycles) or a finite number of times (value between 1 and 999 for total_cycles).
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-payment_preferences
|
||||||
|
PaymentPreferences struct {
|
||||||
|
AutoBillOutstanding bool `json:"auto_bill_outstanding"`
|
||||||
|
SetupFee *Money `json:"setup_fee"`
|
||||||
|
SetupFeeFailureAction SetupFeeFailureAction `json:"setup_fee_failure_action"`
|
||||||
|
PaymentFailureThreshold int `json:"payment_failure_threshold"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PricingScheme struct {
|
||||||
|
Version int `json:"version"`
|
||||||
|
FixedPrice Money `json:"fixed_price"`
|
||||||
|
CreateTime time.Time `json:"create_time"`
|
||||||
|
UpdateTime time.Time `json:"update_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PricingSchemeUpdateRequest struct {
|
||||||
|
Schemes []PricingSchemeUpdate `json:"pricing_schemes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PricingSchemeUpdate struct {
|
||||||
|
BillingCycleSequence int `json:"billing_cycle_sequence"`
|
||||||
|
PricingScheme PricingScheme `json:"pricing_scheme"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-frequency
|
||||||
|
Frequency struct {
|
||||||
|
IntervalUnit IntervalUnit `json:"interval_unit"`
|
||||||
|
IntervalCount int `json:"interval_count"` //different per unit. check documentation
|
||||||
|
}
|
||||||
|
|
||||||
|
Taxes struct {
|
||||||
|
Percentage string `json:"percentage"`
|
||||||
|
Inclusive bool `json:"inclusive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateSubscriptionPlanResponse struct {
|
||||||
|
SubscriptionPlan
|
||||||
|
SharedResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
SubscriptionPlanListParameters struct {
|
||||||
|
ProductId string `json:"product_id"`
|
||||||
|
PlanIds string `json:"plan_ids"` // Filters the response by list of plan IDs. Filter supports upto 10 plan IDs.
|
||||||
|
ListParams
|
||||||
|
}
|
||||||
|
|
||||||
|
ListSubscriptionPlansResponse struct {
|
||||||
|
Plans []SubscriptionPlan `json:"plans"`
|
||||||
|
SharedListResponse
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *SubscriptionPlan) GetUpdatePatch() []Patch {
|
||||||
|
result := []Patch{
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/description",
|
||||||
|
Value: p.Description,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Taxes != nil {
|
||||||
|
result = append(result, Patch{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/taxes/percentage",
|
||||||
|
Value: p.Taxes.Percentage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.PaymentPreferences != nil {
|
||||||
|
if p.PaymentPreferences.SetupFee != nil {
|
||||||
|
result = append(result, Patch{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/setup_fee",
|
||||||
|
Value: p.PaymentPreferences.SetupFee,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, []Patch{{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/auto_bill_outstanding",
|
||||||
|
Value: p.PaymentPreferences.AutoBillOutstanding,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/payment_failure_threshold",
|
||||||
|
Value: p.PaymentPreferences.PaymentFailureThreshold,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/setup_fee_failure_action",
|
||||||
|
Value: p.PaymentPreferences.SetupFeeFailureAction,
|
||||||
|
}}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSubscriptionPlan creates a subscriptionPlan
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_create
|
||||||
|
// Endpoint: POST /v1/billing/plans
|
||||||
|
func (c *Client) CreateSubscriptionPlan(ctx context.Context, newPlan SubscriptionPlan) (*CreateSubscriptionPlanResponse, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/billing/plans"), newPlan)
|
||||||
|
response := &CreateSubscriptionPlanResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSubscriptionPlan. updates a plan
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_patch
|
||||||
|
// Endpoint: PATCH /v1/billing/plans/:plan_id
|
||||||
|
func (c *Client) UpdateSubscriptionPlan(ctx context.Context, updatedPlan SubscriptionPlan) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/billing/plans/", updatedPlan.ID), updatedPlan.GetUpdatePatch())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSubscriptionPlan. updates a plan
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_get
|
||||||
|
// Endpoint: GET /v1/billing/plans/:plan_id
|
||||||
|
func (c *Client) GetSubscriptionPlan(ctx context.Context, planId string) (*SubscriptionPlan, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/billing/plans/", planId), nil)
|
||||||
|
response := &SubscriptionPlan{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all plans
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_list
|
||||||
|
// Endpoint: GET /v1/billing/plans
|
||||||
|
func (c *Client) ListSubscriptionPlans(ctx context.Context, params *SubscriptionPlanListParameters) (*ListSubscriptionPlansResponse, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/billing/plans"), nil)
|
||||||
|
response := &ListSubscriptionPlansResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if params != nil {
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("page", params.Page)
|
||||||
|
q.Add("page_size", params.PageSize)
|
||||||
|
q.Add("total_required", params.TotalRequired)
|
||||||
|
q.Add("product_id", params.ProductId)
|
||||||
|
q.Add("plan_ids", params.PlanIds)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activates a plan
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_activate
|
||||||
|
// Endpoint: POST /v1/billing/plans/{id}/activate
|
||||||
|
func (c *Client) ActivateSubscriptionPlan(ctx context.Context, planId string) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/plans/%s/activate", c.APIBase, planId), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deactivates a plan
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_deactivate
|
||||||
|
// Endpoint: POST /v1/billing/plans/{id}/deactivate
|
||||||
|
func (c *Client) DeactivateSubscriptionPlans(ctx context.Context, planId string) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/plans/%s/deactivate", c.APIBase, planId), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates pricing for a plan. For example, you can update a regular billing cycle from $5 per month to $7 per month.
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#plans_update-pricing-schemes
|
||||||
|
// Endpoint: POST /v1/billing/plans/{id}/update-pricing-schemes
|
||||||
|
func (c *Client) UpdateSubscriptionPlanPricing(ctx context.Context, planId string, pricingSchemes []PricingSchemeUpdate) error {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/plans/%s/update-pricing-schemes", c.APIBase, planId), PricingSchemeUpdateRequest{
|
||||||
|
Schemes: pricingSchemes,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -30,17 +31,15 @@ type TransactionSearchResponse struct {
|
||||||
EndDate JSONTime `json:"end_date"`
|
EndDate JSONTime `json:"end_date"`
|
||||||
LastRefreshDatetime JSONTime `json:"last_refreshed_datetime"`
|
LastRefreshDatetime JSONTime `json:"last_refreshed_datetime"`
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
TotalItems int `json:"total_items"`
|
SharedListResponse
|
||||||
TotalPages int `json:"total_pages"`
|
|
||||||
Links []Link `json:"links"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTransactions - Use this to search PayPal transactions from the last 31 days.
|
// ListTransactions - Use this to search PayPal transactions from the last 31 days.
|
||||||
// Endpoint: GET /v1/reporting/transactions
|
// Endpoint: GET /v1/reporting/transactions
|
||||||
func (c *Client) ListTransactions(req *TransactionSearchRequest) (*TransactionSearchResponse, error) {
|
func (c *Client) ListTransactions(ctx context.Context, req *TransactionSearchRequest) (*TransactionSearchResponse, error) {
|
||||||
response := &TransactionSearchResponse{}
|
response := &TransactionSearchResponse{}
|
||||||
|
|
||||||
r, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/reporting/transactions"), nil)
|
r, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/reporting/transactions"), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
647
types.go
647
types.go
|
@ -12,10 +12,10 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// APIBaseSandBox points to the sandbox (for testing) version of the API
|
// APIBaseSandBox points to the sandbox (for testing) version of the API
|
||||||
APIBaseSandBox = "https://api.sandbox.paypal.com"
|
APIBaseSandBox = "https://api-m.sandbox.paypal.com"
|
||||||
|
|
||||||
// APIBaseLive points to the live version of the API
|
// APIBaseLive points to the live version of the API
|
||||||
APIBaseLive = "https://api.paypal.com"
|
APIBaseLive = "https://api-m.paypal.com"
|
||||||
|
|
||||||
// RequestNewTokenBeforeExpiresIn is used by SendWithAuth and try to get new Token when it's about to expire
|
// RequestNewTokenBeforeExpiresIn is used by SendWithAuth and try to get new Token when it's about to expire
|
||||||
RequestNewTokenBeforeExpiresIn = time.Duration(60) * time.Second
|
RequestNewTokenBeforeExpiresIn = time.Duration(60) * time.Second
|
||||||
|
@ -85,13 +85,9 @@ const (
|
||||||
// Possible values for `shipping_preference` in ApplicationContext
|
// Possible values for `shipping_preference` in ApplicationContext
|
||||||
//
|
//
|
||||||
// https://developer.paypal.com/docs/api/orders/v2/#definition-application_context
|
// https://developer.paypal.com/docs/api/orders/v2/#definition-application_context
|
||||||
const (
|
|
||||||
ShippingPreferenceGetFromFile string = "GET_FROM_FILE"
|
|
||||||
ShippingPreferenceNoShipping string = "NO_SHIPPING"
|
|
||||||
ShippingPreferenceSetProvidedAddress string = "SET_PROVIDED_ADDRESS"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
EventCheckoutOrderApproved string = "CHECKOUT.ORDER.APPROVED"
|
||||||
EventPaymentCaptureCompleted string = "PAYMENT.CAPTURE.COMPLETED"
|
EventPaymentCaptureCompleted string = "PAYMENT.CAPTURE.COMPLETED"
|
||||||
EventPaymentCaptureDenied string = "PAYMENT.CAPTURE.DENIED"
|
EventPaymentCaptureDenied string = "PAYMENT.CAPTURE.DENIED"
|
||||||
EventPaymentCaptureRefunded string = "PAYMENT.CAPTURE.REFUNDED"
|
EventPaymentCaptureRefunded string = "PAYMENT.CAPTURE.REFUNDED"
|
||||||
|
@ -120,6 +116,31 @@ const (
|
||||||
FeatureUpdateCustomerDispute string = "UPDATE_CUSTOMER_DISPUTES"
|
FeatureUpdateCustomerDispute string = "UPDATE_CUSTOMER_DISPUTES"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// https://developer.paypal.com/docs/api/payments.payouts-batch/v1/?mark=recipient_type#definition-recipient_type
|
||||||
|
const (
|
||||||
|
EmailRecipientType string = "EMAIL" // An unencrypted email — string of up to 127 single-byte characters.
|
||||||
|
PaypalIdRecipientType string = "PAYPAL_ID" // An encrypted PayPal account number.
|
||||||
|
PhoneRecipientType string = "PHONE" // An unencrypted phone number.
|
||||||
|
// Note: The PayPal sandbox doesn't support type PHONE
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://developer.paypal.com/docs/api/payments.payouts-batch/v1/?mark=recipient_wallet#definition-recipient_wallet
|
||||||
|
const (
|
||||||
|
PaypalRecipientWallet string = "PAYPAL"
|
||||||
|
VenmoRecipientWallet string = "VENMO"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Possible value for `batch_status` in GetPayout
|
||||||
|
//
|
||||||
|
// https://developer.paypal.com/docs/api/payments.payouts-batch/v1/#definition-batch_status
|
||||||
|
const (
|
||||||
|
BatchStatusDenied string = "DENIED"
|
||||||
|
BatchStatusPending string = "PENDING"
|
||||||
|
BatchStatusProcessing string = "PROCESSING"
|
||||||
|
BatchStatusSuccess string = "SUCCESS"
|
||||||
|
BatchStatusCanceled string = "CANCELED"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LinkRelSelf string = "self"
|
LinkRelSelf string = "self"
|
||||||
LinkRelActionURL string = "action_url"
|
LinkRelActionURL string = "action_url"
|
||||||
|
@ -136,10 +157,10 @@ type (
|
||||||
|
|
||||||
// Address struct
|
// Address struct
|
||||||
Address struct {
|
Address struct {
|
||||||
Line1 string `json:"line1"`
|
Line1 string `json:"line1,omitempty"`
|
||||||
Line2 string `json:"line2,omitempty"`
|
Line2 string `json:"line2,omitempty"`
|
||||||
City string `json:"city"`
|
City string `json:"city,omitempty"`
|
||||||
CountryCode string `json:"country_code"`
|
CountryCode string `json:"country_code,omitempty"`
|
||||||
PostalCode string `json:"postal_code,omitempty"`
|
PostalCode string `json:"postal_code,omitempty"`
|
||||||
State string `json:"state,omitempty"`
|
State string `json:"state,omitempty"`
|
||||||
Phone string `json:"phone,omitempty"`
|
Phone string `json:"phone,omitempty"`
|
||||||
|
@ -171,14 +192,280 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplicationContext struct
|
// ApplicationContext struct
|
||||||
|
//Doc: https://developer.paypal.com/docs/api/orders/v2/#definition-application_context
|
||||||
ApplicationContext struct {
|
ApplicationContext struct {
|
||||||
BrandName string `json:"brand_name,omitempty"`
|
BrandName string `json:"brand_name,omitempty"`
|
||||||
Locale string `json:"locale,omitempty"`
|
Locale string `json:"locale,omitempty"`
|
||||||
LandingPage string `json:"landing_page,omitempty"`
|
ShippingPreference ShippingPreference `json:"shipping_preference,omitempty"`
|
||||||
ShippingPreference string `json:"shipping_preference,omitempty"`
|
UserAction UserAction `json:"user_action,omitempty"`
|
||||||
UserAction string `json:"user_action,omitempty"`
|
PaymentMethod PaymentMethod `json:"payment_method,omitempty"`
|
||||||
ReturnURL string `json:"return_url,omitempty"`
|
LandingPage string `json:"landing_page,omitempty"`
|
||||||
CancelURL string `json:"cancel_url,omitempty"`
|
ReturnURL string `json:"return_url,omitempty"`
|
||||||
|
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"`
|
||||||
|
StandardEntryClassCode StandardEntryClassCode `json:"standard_entry_class_code,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorization struct
|
// Authorization struct
|
||||||
|
@ -196,14 +483,13 @@ type (
|
||||||
Links []Link `json:"links,omitempty"`
|
Links []Link `json:"links,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizeOrderResponse .
|
|
||||||
AuthorizeOrderResponse struct {
|
AuthorizeOrderResponse struct {
|
||||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
Intent string `json:"intent,omitempty"`
|
Intent string `json:"intent,omitempty"`
|
||||||
PurchaseUnits []PurchaseUnitRequest `json:"purchase_units,omitempty"`
|
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
||||||
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,12 +520,6 @@ type (
|
||||||
FinalCapture bool `json:"final_capture,omitempty"`
|
FinalCapture bool `json:"final_capture,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
RefundRequest struct {
|
|
||||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
|
||||||
InvoiceID string `json:"invoice_id,omitempty"`
|
|
||||||
NoteToPayer string `json:"note_to_payer,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
SellerProtection struct {
|
SellerProtection struct {
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
DisputeCategories []string `json:"dispute_categories,omitempty"`
|
DisputeCategories []string `json:"dispute_categories,omitempty"`
|
||||||
|
@ -250,10 +530,6 @@ type (
|
||||||
Reason string `json:"reason,omitempty"`
|
Reason string `json:"reason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
RefundStatusDetails struct {
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
PaymentCaptureResponse struct {
|
PaymentCaptureResponse struct {
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
StatusDetails *CaptureStatusDetails `json:"status_details,omitempty"`
|
StatusDetails *CaptureStatusDetails `json:"status_details,omitempty"`
|
||||||
|
@ -265,11 +541,33 @@ type (
|
||||||
Links []Link `json:"links,omitempty"`
|
Links []Link `json:"links,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//https://developer.paypal.com/docs/api/payments/v2/#captures_get
|
||||||
|
CaptureDetailsResponse struct {
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
StatusDetails *CaptureStatusDetails `json:"status_details,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Amount *Money `json:"amount,omitempty"`
|
||||||
|
InvoiceID string `json:"invoice_id,omitempty"`
|
||||||
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
|
SellerProtection *SellerProtection `json:"seller_protection,omitempty"`
|
||||||
|
FinalCapture bool `json:"final_capture,omitempty"`
|
||||||
|
SellerReceivableBreakdown *SellerReceivableBreakdown `json:"seller_receivable_breakdown,omitempty"`
|
||||||
|
DisbursementMode string `json:"disbursement_mode,omitempty"`
|
||||||
|
Links []Link `json:"links,omitempty"`
|
||||||
|
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||||
|
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// CaptureOrderRequest - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
// CaptureOrderRequest - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
||||||
CaptureOrderRequest struct {
|
CaptureOrderRequest struct {
|
||||||
PaymentSource *PaymentSource `json:"payment_source"`
|
PaymentSource *PaymentSource `json:"payment_source"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CaptureOrderMockResponse - https://developer.paypal.com/docs/api-basics/sandbox/request-headers/#test-api-error-handling-routines
|
||||||
|
CaptureOrderMockResponse struct {
|
||||||
|
MockApplicationCodes string `json:"mock_application_codes"`
|
||||||
|
}
|
||||||
|
|
||||||
// RefundOrderRequest - https://developer.paypal.com/docs/api/payments/v2/#captures_refund
|
// RefundOrderRequest - https://developer.paypal.com/docs/api/payments/v2/#captures_refund
|
||||||
RefundCaptureRequest struct {
|
RefundCaptureRequest struct {
|
||||||
Amount *Money `json:"amount,omitempty"`
|
Amount *Money `json:"amount,omitempty"`
|
||||||
|
@ -288,15 +586,14 @@ type (
|
||||||
SenderBatchHeader *SenderBatchHeader `json:"sender_batch_header,omitempty"`
|
SenderBatchHeader *SenderBatchHeader `json:"sender_batch_header,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BillingAgreement struct
|
// Plan struct
|
||||||
BillingAgreement struct {
|
Plan struct {
|
||||||
Name string `json:"name,omitempty"`
|
ID string `json:"id"`
|
||||||
Description string `json:"description,omitempty"`
|
Name string `json:"name"`
|
||||||
StartDate JSONTime `json:"start_date,omitempty"`
|
Description string `json:"description"`
|
||||||
Plan BillingPlan `json:"plan,omitempty"`
|
CreateTime string `json:"create_time,omitempty"`
|
||||||
Payer Payer `json:"payer,omitempty"`
|
UpdateTime string `json:"update_time,omitempty"`
|
||||||
ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"`
|
PaymentDefinitions []PaymentDefinition `json:"payment_definitions,omitempty"`
|
||||||
OverrideMerchantPreferences *MerchantPreferences `json:"override_merchant_preferences,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BillingInfo struct
|
// BillingInfo struct
|
||||||
|
@ -320,13 +617,14 @@ type (
|
||||||
|
|
||||||
// Capture struct
|
// Capture struct
|
||||||
Capture struct {
|
Capture struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
Amount *Amount `json:"amount,omitempty"`
|
Amount *Amount `json:"amount,omitempty"`
|
||||||
|
State string `json:"state,omitempty"`
|
||||||
|
ParentPayment string `json:"parent_payment,omitempty"`
|
||||||
|
TransactionFee string `json:"transaction_fee,omitempty"`
|
||||||
IsFinalCapture bool `json:"is_final_capture"`
|
IsFinalCapture bool `json:"is_final_capture"`
|
||||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||||
State string `json:"state,omitempty"`
|
|
||||||
ParentPayment string `json:"parent_payment,omitempty"`
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Links []Link `json:"links,omitempty"`
|
Links []Link `json:"links,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +636,8 @@ type (
|
||||||
|
|
||||||
// Client represents a Paypal REST API Client
|
// Client represents a Paypal REST API Client
|
||||||
Client struct {
|
Client struct {
|
||||||
sync.Mutex
|
// sync.Mutex
|
||||||
|
mu sync.Mutex
|
||||||
Client *http.Client
|
Client *http.Client
|
||||||
ClientID string
|
ClientID string
|
||||||
Secret string
|
Secret string
|
||||||
|
@ -368,10 +667,8 @@ type (
|
||||||
|
|
||||||
// CreditCards GET /v1/vault/credit-cards
|
// CreditCards GET /v1/vault/credit-cards
|
||||||
CreditCards struct {
|
CreditCards struct {
|
||||||
Items []CreditCard `json:"items"`
|
Items []CreditCard `json:"items"`
|
||||||
Links []Link `json:"links"`
|
SharedListResponse
|
||||||
TotalItems int `json:"total_items"`
|
|
||||||
TotalPages int `json:"total_pages"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreditCardToken struct
|
// CreditCardToken struct
|
||||||
|
@ -430,9 +727,12 @@ type (
|
||||||
|
|
||||||
// ErrorResponseDetail struct
|
// ErrorResponseDetail struct
|
||||||
ErrorResponseDetail struct {
|
ErrorResponseDetail struct {
|
||||||
Field string `json:"field"`
|
Field string `json:"field"`
|
||||||
Issue string `json:"issue"`
|
Issue string `json:"issue"`
|
||||||
Links []Link `json:"link"`
|
Name string `json:"name"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Links []Link `json:"link"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorResponse https://developer.paypal.com/docs/api/errors/
|
// ErrorResponse https://developer.paypal.com/docs/api/errors/
|
||||||
|
@ -525,10 +825,19 @@ type (
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PurchaseUnit struct
|
|
||||||
PurchaseUnit struct {
|
PurchaseUnit struct {
|
||||||
ReferenceID string `json:"reference_id"`
|
ReferenceID string `json:"reference_id"`
|
||||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
||||||
|
Payee *PayeeForOrders `json:"payee,omitempty"`
|
||||||
|
Payments *CapturedPayments `json:"payments,omitempty"`
|
||||||
|
PaymentInstruction *PaymentInstruction `json:"payment_instruction,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
|
InvoiceID string `json:"invoice_id,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
SoftDescriptor string `json:"soft_descriptor,omitempty"`
|
||||||
|
Shipping *ShippingDetail `json:"shipping,omitempty"`
|
||||||
|
Items []Item `json:"items,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaxInfo used for orders.
|
// TaxInfo used for orders.
|
||||||
|
@ -567,15 +876,16 @@ type (
|
||||||
|
|
||||||
// PurchaseUnitRequest struct
|
// PurchaseUnitRequest struct
|
||||||
PurchaseUnitRequest struct {
|
PurchaseUnitRequest struct {
|
||||||
ReferenceID string `json:"reference_id,omitempty"`
|
ReferenceID string `json:"reference_id,omitempty"`
|
||||||
Amount *PurchaseUnitAmount `json:"amount"`
|
Amount *PurchaseUnitAmount `json:"amount"`
|
||||||
Payee *PayeeForOrders `json:"payee,omitempty"`
|
Payee *PayeeForOrders `json:"payee,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
CustomID string `json:"custom_id,omitempty"`
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
InvoiceID string `json:"invoice_id,omitempty"`
|
InvoiceID string `json:"invoice_id,omitempty"`
|
||||||
SoftDescriptor string `json:"soft_descriptor,omitempty"`
|
SoftDescriptor string `json:"soft_descriptor,omitempty"`
|
||||||
Items []Item `json:"items,omitempty"`
|
Items []Item `json:"items,omitempty"`
|
||||||
Shipping *ShippingDetail `json:"shipping,omitempty"`
|
Shipping *ShippingDetail `json:"shipping,omitempty"`
|
||||||
|
PaymentInstruction *PaymentInstruction `json:"payment_instruction,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MerchantPreferences struct
|
// MerchantPreferences struct
|
||||||
|
@ -590,25 +900,50 @@ type (
|
||||||
|
|
||||||
// Order struct
|
// Order struct
|
||||||
Order struct {
|
Order struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
Intent string `json:"intent,omitempty"`
|
Intent string `json:"intent,omitempty"`
|
||||||
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
||||||
Links []Link `json:"links,omitempty"`
|
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
||||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
Links []Link `json:"links,omitempty"`
|
||||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||||
|
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExchangeRate struct
|
||||||
|
//
|
||||||
|
// https://developer.paypal.com/docs/api/orders/v2/#definition-exchange_rate
|
||||||
|
ExchangeRate struct {
|
||||||
|
SourceCurrency string `json:"source_currency"`
|
||||||
|
TargetCurrency string `json:"target_currency"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SellerReceivableBreakdown has the detailed breakdown of the capture activity.
|
||||||
|
SellerReceivableBreakdown struct {
|
||||||
|
GrossAmount *Money `json:"gross_amount,omitempty"`
|
||||||
|
PaypalFee *Money `json:"paypal_fee,omitempty"`
|
||||||
|
PaypalFeeInReceivableCurrency *Money `json:"paypal_fee_in_receivable_currency,omitempty"`
|
||||||
|
NetAmount *Money `json:"net_amount,omitempty"`
|
||||||
|
ReceivableAmount *Money `json:"receivable_amount,omitempty"`
|
||||||
|
ExchangeRate *ExchangeRate `json:"exchange_rate,omitempty"`
|
||||||
|
PlatformFees []PlatformFee `json:"platform_fees,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureAmount struct
|
// CaptureAmount struct
|
||||||
CaptureAmount struct {
|
CaptureAmount struct {
|
||||||
ID string `json:"id,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
CustomID string `json:"custom_id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
|
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
||||||
|
SellerProtection *SellerProtection `json:"seller_protection,omitempty"`
|
||||||
|
SellerReceivableBreakdown *SellerReceivableBreakdown `json:"seller_receivable_breakdown,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CapturedPayments has the amounts for a captured order
|
// CapturedPayments has the amounts for a captured order
|
||||||
CapturedPayments struct {
|
CapturedPayments struct {
|
||||||
Captures []CaptureAmount `json:"captures,omitempty"`
|
Autthorizations []Authorization `json:"authorizations,omitempty"`
|
||||||
|
Captures []CaptureAmount `json:"captures,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CapturedPurchaseItem are items for a captured order
|
// CapturedPurchaseItem are items for a captured order
|
||||||
|
@ -633,10 +968,13 @@ type (
|
||||||
|
|
||||||
// PayerWithNameAndPhone struct
|
// PayerWithNameAndPhone struct
|
||||||
PayerWithNameAndPhone struct {
|
PayerWithNameAndPhone struct {
|
||||||
Name *CreateOrderPayerName `json:"name,omitempty"`
|
Name *CreateOrderPayerName `json:"name,omitempty"`
|
||||||
EmailAddress string `json:"email_address,omitempty"`
|
EmailAddress string `json:"email_address,omitempty"`
|
||||||
Phone *PhoneWithType `json:"phone,omitempty"`
|
Phone *PhoneWithType `json:"phone,omitempty"`
|
||||||
PayerID string `json:"payer_id,omitempty"`
|
PayerID string `json:"payer_id,omitempty"`
|
||||||
|
BirthDate string `json:"birth_date,omitempty"`
|
||||||
|
TaxInfo *TaxInfo `json:"tax_info,omitempty"`
|
||||||
|
Address *ShippingDetailAddressPortable `json:"address,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureOrderResponse is the response for capture order
|
// CaptureOrderResponse is the response for capture order
|
||||||
|
@ -644,6 +982,7 @@ type (
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
||||||
|
Address *Address `json:"address,omitempty"`
|
||||||
PurchaseUnits []CapturedPurchaseUnit `json:"purchase_units,omitempty"`
|
PurchaseUnits []CapturedPurchaseUnit `json:"purchase_units,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,27 +1050,44 @@ type (
|
||||||
|
|
||||||
// PaymentSource structure
|
// PaymentSource structure
|
||||||
PaymentSource struct {
|
PaymentSource struct {
|
||||||
Card *PaymentSourceCard `json:"card"`
|
Card *PaymentSourceCard `json:"card,omitempty"`
|
||||||
Token *PaymentSourceToken `json:"token"`
|
Token *PaymentSourceToken `json:"token,omitempty"`
|
||||||
|
Paypal *PaymentSourcePaypal `json:"paypal,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaymentSourceCard structure
|
// PaymentSourceCard structure
|
||||||
PaymentSourceCard struct {
|
PaymentSourceCard struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name,omitempty"`
|
||||||
Number string `json:"number"`
|
Number string `json:"number,omitempty"`
|
||||||
Expiry string `json:"expiry"`
|
Expiry string `json:"expiry,omitempty"`
|
||||||
SecurityCode string `json:"security_code"`
|
SecurityCode string `json:"security_code,omitempty"`
|
||||||
LastDigits string `json:"last_digits"`
|
LastDigits string `json:"last_digits,omitempty"`
|
||||||
CardType string `json:"card_type"`
|
CardType string `json:"card_type,omitempty"`
|
||||||
BillingAddress *CardBillingAddress `json:"billing_address"`
|
BillingAddress *CardBillingAddress `json:"billing_address,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaymentSourcePaypal structure
|
||||||
|
PaymentSourcePaypal struct {
|
||||||
|
ExperienceContext PaymentSourcePaypalExperienceContext `json:"experience_context"`
|
||||||
|
}
|
||||||
|
|
||||||
|
PaymentSourcePaypalExperienceContext struct {
|
||||||
|
PaymentMethodPreference string `json:"payment_method_preference"`
|
||||||
|
BrandName string `json:"brand_name"`
|
||||||
|
Locale string `json:"locale"`
|
||||||
|
LandingPage string `json:"landing_page"`
|
||||||
|
ShippingPreference string `json:"shipping_preference"`
|
||||||
|
UserAction string `json:"user_action"`
|
||||||
|
ReturnURL string `json:"return_url"`
|
||||||
|
CancelURL string `json:"cancel_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CardBillingAddress structure
|
// CardBillingAddress structure
|
||||||
CardBillingAddress struct {
|
CardBillingAddress struct {
|
||||||
AddressLine1 string `json:"address_line_1"`
|
AddressLine1 string `json:"address_line_1"`
|
||||||
AddressLine2 string `json:"address_line_2"`
|
AddressLine2 string `json:"address_line_2,omitempty"`
|
||||||
AdminArea2 string `json:"admin_area_2"`
|
AdminArea2 string `json:"admin_area_2,omitempty"`
|
||||||
AdminArea1 string `json:"admin_area_1"`
|
AdminArea1 string `json:"admin_area_1"`
|
||||||
PostalCode string `json:"postal_code"`
|
PostalCode string `json:"postal_code"`
|
||||||
CountryCode string `json:"country_code"`
|
CountryCode string `json:"country_code"`
|
||||||
|
@ -751,11 +1107,12 @@ type (
|
||||||
|
|
||||||
// PayoutItem struct
|
// PayoutItem struct
|
||||||
PayoutItem struct {
|
PayoutItem struct {
|
||||||
RecipientType string `json:"recipient_type"`
|
RecipientType string `json:"recipient_type"`
|
||||||
Receiver string `json:"receiver"`
|
RecipientWallet string `json:"recipient_wallet"`
|
||||||
Amount *AmountPayout `json:"amount"`
|
Receiver string `json:"receiver"`
|
||||||
Note string `json:"note,omitempty"`
|
Amount *AmountPayout `json:"amount"`
|
||||||
SenderItemID string `json:"sender_item_id,omitempty"`
|
Note string `json:"note,omitempty"`
|
||||||
|
SenderItemID string `json:"sender_item_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PayoutItemResponse struct
|
// PayoutItemResponse struct
|
||||||
|
@ -786,16 +1143,14 @@ type (
|
||||||
|
|
||||||
// Refund struct
|
// Refund struct
|
||||||
Refund struct {
|
Refund struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
Amount *Amount `json:"amount,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||||
StatusDetails *RefundStatusDetails `json:"status_details,omitempty"`
|
State string `json:"state,omitempty"`
|
||||||
InvoiceID string `json:"invoice_id,omitempty"`
|
CaptureID string `json:"capture_id,omitempty"`
|
||||||
NoteToPayer string `json:"note_to_payer,omitempty"`
|
ParentPayment string `json:"parent_payment,omitempty"`
|
||||||
SellerPayableBreakdown *CaptureSellerBreakdown `json:"seller_payable_breakdown,omitempty"`
|
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||||
Links []Link `json:"links"`
|
SaleID string `json:"sale_id,omitempty"`
|
||||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
|
||||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefundResponse .
|
// RefundResponse .
|
||||||
|
@ -803,6 +1158,7 @@ type (
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
|
Links []Link `json:"links,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Related struct
|
// Related struct
|
||||||
|
@ -842,8 +1198,7 @@ type (
|
||||||
|
|
||||||
//ShippingAmount struct
|
//ShippingAmount struct
|
||||||
ShippingAmount struct {
|
ShippingAmount struct {
|
||||||
CurrencyCode string `json:"currency_code,omitempty"`
|
Money
|
||||||
Value string `json:"value,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShippingAddress struct
|
// ShippingAddress struct
|
||||||
|
@ -870,8 +1225,14 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name struct
|
// Name struct
|
||||||
|
//Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-name
|
||||||
Name struct {
|
Name struct {
|
||||||
FullName string `json:"full_name,omitempty"`
|
FullName string `json:"full_name,omitempty"`
|
||||||
|
Suffix string `json:"suffix,omitempty"`
|
||||||
|
Prefix string `json:"prefix,omitempty"`
|
||||||
|
GivenName string `json:"given_name,omitempty"`
|
||||||
|
Surname string `json:"surname,omitempty"`
|
||||||
|
MiddleName string `json:"middle_name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShippingDetail struct
|
// ShippingDetail struct
|
||||||
|
@ -882,6 +1243,7 @@ type (
|
||||||
|
|
||||||
// Subscriber struct
|
// Subscriber struct
|
||||||
Subscriber struct {
|
Subscriber struct {
|
||||||
|
PayerID string `json:"payer_id"`
|
||||||
ShippingAddress ShippingDetail `json:"shipping_address,omitempty"`
|
ShippingAddress ShippingDetail `json:"shipping_address,omitempty"`
|
||||||
Name CreateOrderPayerName `json:"name,omitempty"`
|
Name CreateOrderPayerName `json:"name,omitempty"`
|
||||||
EmailAddress string `json:"email_address,omitempty"`
|
EmailAddress string `json:"email_address,omitempty"`
|
||||||
|
@ -988,7 +1350,11 @@ type (
|
||||||
VerificationStatus string `json:"verification_status,omitempty"`
|
VerificationStatus string `json:"verification_status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Webhook strunct
|
WebhookEventTypesResponse struct {
|
||||||
|
EventTypes []WebhookEventType `json:"event_types"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Webhook struct
|
||||||
Webhook struct {
|
Webhook struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
|
@ -996,22 +1362,31 @@ type (
|
||||||
Links []Link `json:"links"`
|
Links []Link `json:"links"`
|
||||||
}
|
}
|
||||||
|
|
||||||
WebhookEvent struct {
|
// Event struct.
|
||||||
|
//
|
||||||
|
// The basic webhook event data type. This struct is intended to be
|
||||||
|
// embedded into resource type specific event structs.
|
||||||
|
Event struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
CreateTime time.Time `json:"create_time"`
|
CreateTime time.Time `json:"create_time"`
|
||||||
ResourceType string `json:"resource_type"`
|
ResourceType string `json:"resource_type"`
|
||||||
EventType string `json:"event_type"`
|
EventType string `json:"event_type"`
|
||||||
Summary string `json:"summary,omitempty"`
|
Summary string `json:"summary,omitempty"`
|
||||||
Resource Resource `json:"resource"`
|
|
||||||
Links []Link `json:"links"`
|
Links []Link `json:"links"`
|
||||||
EventVersion string `json:"event_version,omitempty"`
|
EventVersion string `json:"event_version,omitempty"`
|
||||||
ResourceVersion string `json:"resource_version,omitempty"`
|
ResourceVersion string `json:"resource_version,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnyEvent struct {
|
||||||
|
Event
|
||||||
|
Resource json.RawMessage `json:"resource"`
|
||||||
|
}
|
||||||
|
|
||||||
// WebhookEventType struct
|
// WebhookEventType struct
|
||||||
WebhookEventType struct {
|
WebhookEventType struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateWebhookRequest struct
|
// CreateWebhookRequest struct
|
||||||
|
@ -1030,26 +1405,6 @@ type (
|
||||||
Value interface{} `json:"value"`
|
Value interface{} `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource struct {
|
|
||||||
// Payment Resource type
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
StatusDetails *CaptureStatusDetails `json:"status_details,omitempty"`
|
|
||||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
|
||||||
UpdateTime string `json:"update_time,omitempty"`
|
|
||||||
CreateTime string `json:"create_time,omitempty"`
|
|
||||||
ExpirationTime string `json:"expiration_time,omitempty"`
|
|
||||||
SellerProtection *SellerProtection `json:"seller_protection,omitempty"`
|
|
||||||
FinalCapture bool `json:"final_capture,omitempty"`
|
|
||||||
SellerPayableBreakdown *CaptureSellerBreakdown `json:"seller_payable_breakdown,omitempty"`
|
|
||||||
NoteToPayer string `json:"note_to_payer,omitempty"`
|
|
||||||
// merchant-onboarding Resource type
|
|
||||||
PartnerClientID string `json:"partner_client_id,omitempty"`
|
|
||||||
MerchantID string `json:"merchant_id,omitempty"`
|
|
||||||
// Common
|
|
||||||
Links []Link `json:"links,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
CaptureSellerBreakdown struct {
|
CaptureSellerBreakdown struct {
|
||||||
GrossAmount PurchaseUnitAmount `json:"gross_amount"`
|
GrossAmount PurchaseUnitAmount `json:"gross_amount"`
|
||||||
PayPalFee PurchaseUnitAmount `json:"paypal_fee"`
|
PayPalFee PurchaseUnitAmount `json:"paypal_fee"`
|
||||||
|
@ -1065,6 +1420,10 @@ type (
|
||||||
LegalConsents []Consent `json:"legal_consents,omitempty"`
|
LegalConsents []Consent `json:"legal_consents,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReferralResponse struct {
|
||||||
|
Links []Link `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
PartnerConfigOverride struct {
|
PartnerConfigOverride struct {
|
||||||
PartnerLogoURL string `json:"partner_logo_url,omitempty"`
|
PartnerLogoURL string `json:"partner_logo_url,omitempty"`
|
||||||
ReturnURL string `json:"return_url,omitempty"`
|
ReturnURL string `json:"return_url,omitempty"`
|
||||||
|
@ -1196,6 +1555,30 @@ type (
|
||||||
ShippingInfo *SearchShippingInfo `json:"shipping_info"`
|
ShippingInfo *SearchShippingInfo `json:"shipping_info"`
|
||||||
CartInfo *SearchCartInfo `json:"cart_info"`
|
CartInfo *SearchCartInfo `json:"cart_info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedResponse struct {
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
UpdateTime string `json:"update_time"`
|
||||||
|
Links []Link `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ListParams struct {
|
||||||
|
Page string `json:"page,omitempty"` //Default: 0.
|
||||||
|
PageSize string `json:"page_size,omitempty"` //Default: 10.
|
||||||
|
TotalRequired string `json:"total_required,omitempty"` //Default: no.
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedListResponse struct {
|
||||||
|
TotalItems int `json:"total_items,omitempty"`
|
||||||
|
TotalPages int `json:"total_pages,omitempty"`
|
||||||
|
Links []Link `json:"links,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Patch struct {
|
||||||
|
Operation string `json:"op"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error method implementation for ErrorResponse struct
|
// Error method implementation for ErrorResponse struct
|
||||||
|
@ -1230,3 +1613,9 @@ func (e *expirationTime) UnmarshalJSON(b []byte) error {
|
||||||
*e = expirationTime(i)
|
*e = expirationTime(i)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert ExpirationTime to time.Duration
|
||||||
|
func (e *expirationTime) ToDuration() time.Duration {
|
||||||
|
seconds := int64(*e)
|
||||||
|
return time.Duration(seconds) * time.Second
|
||||||
|
}
|
||||||
|
|
514
unit_test.go
514
unit_test.go
|
@ -2,16 +2,9 @@ package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type webprofileTestServer struct {
|
|
||||||
t *testing.T
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewClient(t *testing.T) {
|
func TestNewClient(t *testing.T) {
|
||||||
c, err := NewClient("", "", "")
|
c, err := NewClient("", "", "")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -140,6 +133,35 @@ func TestTypeErrorResponseTwo(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTypeErrorResponseThree(t *testing.T) {
|
||||||
|
response := `{
|
||||||
|
"name": "BUSINESS_ERROR",
|
||||||
|
"debug_id": "[REDACTED]",
|
||||||
|
"message": "Business error",
|
||||||
|
"information_link": "https://developer.paypal.com/webapps/developer/docs/api/#BUSINESS_ERROR",
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"name": "TOKEN_NOT_FOUND",
|
||||||
|
"message": "Not Found: Invalid BA-Token Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
i := &ErrorResponse{}
|
||||||
|
err := json.Unmarshal([]byte(response), i)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ErrorResponse Unmarshal failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.Name != "BUSINESS_ERROR" ||
|
||||||
|
i.Message != "Business error" ||
|
||||||
|
len(i.Details) != 1 ||
|
||||||
|
i.Details[0].Name != "TOKEN_NOT_FOUND" ||
|
||||||
|
i.Details[0].Message != "Not Found: Invalid BA-Token Identifier" {
|
||||||
|
t.Errorf("ErrorResponse decoded result is incorrect, Given: %v", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTypePayoutResponse(t *testing.T) {
|
func TestTypePayoutResponse(t *testing.T) {
|
||||||
response := `{
|
response := `{
|
||||||
"batch_header":{
|
"batch_header":{
|
||||||
|
@ -260,6 +282,98 @@ func TestOrderUnmarshal(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOrderCompletedUnmarshal(t *testing.T) {
|
||||||
|
response := `{
|
||||||
|
"id": "1K412082HD5737736",
|
||||||
|
"status": "COMPLETED",
|
||||||
|
"purchase_units": [
|
||||||
|
{
|
||||||
|
"reference_id": "default",
|
||||||
|
"amount": {
|
||||||
|
"currency_code": "EUR",
|
||||||
|
"value": "99.99"
|
||||||
|
},
|
||||||
|
"payee": {
|
||||||
|
"email_address": "payee@business.example.com",
|
||||||
|
"merchant_id": "7DVPP5Q2RZJQY"
|
||||||
|
},
|
||||||
|
"custom_id": "123456",
|
||||||
|
"soft_descriptor": "PAYPAL *TEST STORE",
|
||||||
|
"shipping": {
|
||||||
|
"name": {
|
||||||
|
"full_name": "John Doe"
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"address_line_1": "Address, Country",
|
||||||
|
"admin_area_2": "Area2",
|
||||||
|
"admin_area_1": "Area1",
|
||||||
|
"postal_code": "123456",
|
||||||
|
"country_code": "US"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payments": {
|
||||||
|
"captures": [
|
||||||
|
{
|
||||||
|
"id": "6V864560EH247264J",
|
||||||
|
"status": "COMPLETED",
|
||||||
|
"amount": {
|
||||||
|
"currency_code": "EUR",
|
||||||
|
"value": "99.99"
|
||||||
|
},
|
||||||
|
"final_capture": true,
|
||||||
|
"custom_id": "123456",
|
||||||
|
"create_time": "2021-07-27T09:39:17Z",
|
||||||
|
"update_time": "2021-07-27T09:39:17Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payer": {
|
||||||
|
"name": {
|
||||||
|
"given_name": "John",
|
||||||
|
"surname": "Doe"
|
||||||
|
},
|
||||||
|
"email_address": "payer@personal.example.com",
|
||||||
|
"payer_id": "7D36CJQ2TUEUU",
|
||||||
|
"address": {
|
||||||
|
"address_line_1": "City, Country",
|
||||||
|
"admin_area_2": "Area2",
|
||||||
|
"admin_area_1": "Area1",
|
||||||
|
"postal_code": "123456",
|
||||||
|
"country_code": "US"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"create_time": "2021-07-27T09:38:37Z",
|
||||||
|
"update_time": "2021-07-27T09:39:17Z",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/1K412082HD5737736",
|
||||||
|
"rel": "self",
|
||||||
|
"method": "GET"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
order := &Order{}
|
||||||
|
err := json.Unmarshal([]byte(response), order)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Order Unmarshal failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if order.ID != "1K412082HD5737736" ||
|
||||||
|
order.Status != "COMPLETED" ||
|
||||||
|
order.PurchaseUnits[0].Payee.EmailAddress != "payee@business.example.com" ||
|
||||||
|
order.PurchaseUnits[0].CustomID != "123456" ||
|
||||||
|
order.PurchaseUnits[0].Shipping.Name.FullName != "John Doe" ||
|
||||||
|
order.PurchaseUnits[0].Shipping.Address.AdminArea1 != "Area1" ||
|
||||||
|
order.Payer.Name.GivenName != "John" ||
|
||||||
|
order.Payer.Address.AddressLine1 != "City, Country" ||
|
||||||
|
order.Links[0].Href != "https://api.sandbox.paypal.com/v2/checkout/orders/1K412082HD5737736" {
|
||||||
|
t.Errorf("Order decoded result is incorrect, Given: %+v", order)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTypePayoutItemResponse(t *testing.T) {
|
func TestTypePayoutItemResponse(t *testing.T) {
|
||||||
response := `{
|
response := `{
|
||||||
"payout_item_id":"9T35G83YA546X",
|
"payout_item_id":"9T35G83YA546X",
|
||||||
|
@ -357,389 +471,3 @@ func TestTypePaymentPatchMarshal(t *testing.T) {
|
||||||
t.Errorf("PaymentPatch response2 is incorrect,\n Given: %+v\n Expected: %+v", string(response2), string(p2expectedresponse))
|
t.Errorf("PaymentPatch response2 is incorrect,\n Given: %+v\n Expected: %+v", string(response2), string(p2expectedresponse))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler
|
|
||||||
func (ts *webprofileTestServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ts.t.Log(r.RequestURI)
|
|
||||||
if r.RequestURI == "/v1/payment-experience/web-profiles" {
|
|
||||||
if r.Method == "POST" {
|
|
||||||
ts.create(w, r)
|
|
||||||
}
|
|
||||||
if r.Method == "GET" {
|
|
||||||
ts.list(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.RequestURI == "/v1/payment-experience/web-profiles/XP-CP6S-W9DY-96H8-MVN2" {
|
|
||||||
if r.Method == "GET" {
|
|
||||||
ts.getvalid(w, r)
|
|
||||||
}
|
|
||||||
if r.Method == "PUT" {
|
|
||||||
ts.updatevalid(w, r)
|
|
||||||
}
|
|
||||||
if r.Method == "DELETE" {
|
|
||||||
ts.deletevalid(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.RequestURI == "/v1/payment-experience/web-profiles/foobar" {
|
|
||||||
if r.Method == "GET" {
|
|
||||||
ts.getinvalid(w, r)
|
|
||||||
}
|
|
||||||
if r.Method == "PUT" {
|
|
||||||
ts.updateinvalid(w, r)
|
|
||||||
}
|
|
||||||
if r.Method == "DELETE" {
|
|
||||||
ts.deleteinvalid(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) create(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var data map[string]interface{}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(body, &data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
var raw map[string]string
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
if name, ok := data["name"]; !ok || name == "" {
|
|
||||||
raw = map[string]string{
|
|
||||||
"name": "VALIDATION_ERROR",
|
|
||||||
"message": "should have name",
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
} else {
|
|
||||||
raw = map[string]string{
|
|
||||||
"id": "XP-CP6S-W9DY-96H8-MVN2",
|
|
||||||
}
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) updatevalid(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var data map[string]interface{}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(body, &data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ID, ok := data["id"]; !ok || ID != "XP-CP6S-W9DY-96H8-MVN2" {
|
|
||||||
raw := map[string]string{
|
|
||||||
"name": "INVALID_RESOURCE_ID",
|
|
||||||
"message": "id invalid",
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if name, ok := data["name"]; !ok || name == "" {
|
|
||||||
raw := map[string]string{
|
|
||||||
"name": "VALIDATION_ERROR",
|
|
||||||
"message": "should have name",
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) updateinvalid(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
|
|
||||||
raw := map[string]interface{}{
|
|
||||||
"name": "INVALID_RESOURCE_ID",
|
|
||||||
"message": "foobar not found",
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) getvalid(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
|
|
||||||
raw := map[string]interface{}{
|
|
||||||
"id": "XP-CP6S-W9DY-96H8-MVN2",
|
|
||||||
"name": "YeowZa! T-Shirt Shop",
|
|
||||||
"presentation": map[string]interface{}{
|
|
||||||
"brand_name": "YeowZa! Paypal",
|
|
||||||
"logo_image": "http://www.yeowza.com",
|
|
||||||
"locale_code": "US",
|
|
||||||
},
|
|
||||||
|
|
||||||
"input_fields": map[string]interface{}{
|
|
||||||
"allow_note": true,
|
|
||||||
"no_shipping": 0,
|
|
||||||
"address_override": 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
"flow_config": map[string]interface{}{
|
|
||||||
"landing_page_type": "Billing",
|
|
||||||
"bank_txn_pending_url": "http://www.yeowza.com",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) getinvalid(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
|
|
||||||
raw := map[string]interface{}{
|
|
||||||
"name": "INVALID_RESOURCE_ID",
|
|
||||||
"message": "foobar not found",
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) list(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
|
|
||||||
raw := []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"id": "XP-CP6S-W9DY-96H8-MVN2",
|
|
||||||
"name": "YeowZa! T-Shirt Shop",
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"id": "XP-96H8-MVN2-CP6S-W9DY",
|
|
||||||
"name": "Shop T-Shirt YeowZa! ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) deleteinvalid(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
|
|
||||||
raw := map[string]interface{}{
|
|
||||||
"name": "INVALID_RESOURCE_ID",
|
|
||||||
"message": "foobar not found",
|
|
||||||
}
|
|
||||||
|
|
||||||
res, _ := json.Marshal(raw)
|
|
||||||
w.Write(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *webprofileTestServer) deletevalid(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateWebProfile_valid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
wp := WebProfile{
|
|
||||||
Name: "YeowZa! T-Shirt Shop",
|
|
||||||
Presentation: Presentation{
|
|
||||||
BrandName: "YeowZa! Paypal",
|
|
||||||
LogoImage: "http://www.yeowza.com",
|
|
||||||
LocaleCode: "US",
|
|
||||||
},
|
|
||||||
|
|
||||||
InputFields: InputFields{
|
|
||||||
AllowNote: true,
|
|
||||||
NoShipping: NoShippingDisplay,
|
|
||||||
AddressOverride: AddrOverrideFromCall,
|
|
||||||
},
|
|
||||||
|
|
||||||
FlowConfig: FlowConfig{
|
|
||||||
LandingPageType: LandingPageTypeBilling,
|
|
||||||
BankTXNPendingURL: "http://www.yeowza.com",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := c.CreateWebProfile(wp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.ID != "XP-CP6S-W9DY-96H8-MVN2" {
|
|
||||||
t.Fatalf("expecting response to have ID = `XP-CP6S-W9DY-96H8-MVN2` got `%s`", res.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateWebProfile_invalid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
wp := WebProfile{}
|
|
||||||
|
|
||||||
_, err := c.CreateWebProfile(wp)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expecting an error got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetWebProfile_valid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
res, err := c.GetWebProfile("XP-CP6S-W9DY-96H8-MVN2")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.ID != "XP-CP6S-W9DY-96H8-MVN2" {
|
|
||||||
t.Fatalf("expecting res.ID to have value = `XP-CP6S-W9DY-96H8-MVN2` but got `%s`", res.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Name != "YeowZa! T-Shirt Shop" {
|
|
||||||
t.Fatalf("expecting res.Name to have value = `YeowZa! T-Shirt Shop` but got `%s`", res.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetWebProfile_invalid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
_, err := c.GetWebProfile("foobar")
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expecting an error got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetWebProfiles(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
res, err := c.GetWebProfiles()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(res) != 2 {
|
|
||||||
t.Fatalf("expecting two results got %d", len(res))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetWebProfile_valid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
wp := WebProfile{
|
|
||||||
ID: "XP-CP6S-W9DY-96H8-MVN2",
|
|
||||||
Name: "Shop T-Shirt YeowZa!",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := c.SetWebProfile(wp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetWebProfile_invalid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
wp := WebProfile{
|
|
||||||
ID: "foobar",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := c.SetWebProfile(wp)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wp = WebProfile{}
|
|
||||||
|
|
||||||
err = c.SetWebProfile(wp)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteWebProfile_valid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
wp := WebProfile{
|
|
||||||
ID: "XP-CP6S-W9DY-96H8-MVN2",
|
|
||||||
Name: "Shop T-Shirt YeowZa!",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := c.SetWebProfile(wp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteWebProfile_invalid(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(&webprofileTestServer{t: t})
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
c, _ := NewClient("foo", "bar", ts.URL)
|
|
||||||
|
|
||||||
err := c.DeleteWebProfile("foobar")
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
21
vault.go
21
vault.go
|
@ -1,13 +1,14 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StoreCreditCard func
|
// StoreCreditCard func
|
||||||
// Endpoint: POST /v1/vault/credit-cards
|
// Endpoint: POST /v1/vault/credit-cards
|
||||||
func (c *Client) StoreCreditCard(cc CreditCard) (*CreditCard, error) {
|
func (c *Client) StoreCreditCard(ctx context.Context, cc CreditCard) (*CreditCard, error) {
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/vault/credit-cards"), cc)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/vault/credit-cards"), cc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -23,8 +24,8 @@ func (c *Client) StoreCreditCard(cc CreditCard) (*CreditCard, error) {
|
||||||
|
|
||||||
// DeleteCreditCard func
|
// DeleteCreditCard func
|
||||||
// Endpoint: DELETE /v1/vault/credit-cards/credit_card_id
|
// Endpoint: DELETE /v1/vault/credit-cards/credit_card_id
|
||||||
func (c *Client) DeleteCreditCard(id string) error {
|
func (c *Client) DeleteCreditCard(ctx context.Context, id string) error {
|
||||||
req, err := c.NewRequest("DELETE", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
req, err := c.NewRequest(ctx, "DELETE", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -38,8 +39,8 @@ func (c *Client) DeleteCreditCard(id string) error {
|
||||||
|
|
||||||
// GetCreditCard func
|
// GetCreditCard func
|
||||||
// Endpoint: GET /v1/vault/credit-cards/credit_card_id
|
// Endpoint: GET /v1/vault/credit-cards/credit_card_id
|
||||||
func (c *Client) GetCreditCard(id string) (*CreditCard, error) {
|
func (c *Client) GetCreditCard(ctx context.Context, id string) (*CreditCard, error) {
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,7 @@ func (c *Client) GetCreditCard(id string) (*CreditCard, error) {
|
||||||
|
|
||||||
// GetCreditCards func
|
// GetCreditCards func
|
||||||
// Endpoint: GET /v1/vault/credit-cards
|
// Endpoint: GET /v1/vault/credit-cards
|
||||||
func (c *Client) GetCreditCards(ccf *CreditCardsFilter) (*CreditCards, error) {
|
func (c *Client) GetCreditCards(ctx context.Context, ccf *CreditCardsFilter) (*CreditCards, error) {
|
||||||
page := 1
|
page := 1
|
||||||
if ccf != nil && ccf.Page > 0 {
|
if ccf != nil && ccf.Page > 0 {
|
||||||
page = ccf.Page
|
page = ccf.Page
|
||||||
|
@ -65,7 +66,7 @@ func (c *Client) GetCreditCards(ccf *CreditCardsFilter) (*CreditCards, error) {
|
||||||
pageSize = ccf.PageSize
|
pageSize = ccf.PageSize
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s/v1/vault/credit-cards?page=%d&page_size=%d", c.APIBase, page, pageSize), nil)
|
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s/v1/vault/credit-cards?page=%d&page_size=%d", c.APIBase, page, pageSize), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -81,8 +82,8 @@ func (c *Client) GetCreditCards(ccf *CreditCardsFilter) (*CreditCards, error) {
|
||||||
|
|
||||||
// PatchCreditCard func
|
// PatchCreditCard func
|
||||||
// Endpoint: PATCH /v1/vault/credit-cards/credit_card_id
|
// Endpoint: PATCH /v1/vault/credit-cards/credit_card_id
|
||||||
func (c *Client) PatchCreditCard(id string, ccf []CreditCardField) (*CreditCard, error) {
|
func (c *Client) PatchCreditCard(ctx context.Context, id string, ccf []CreditCardField) (*CreditCard, error) {
|
||||||
req, err := c.NewRequest("PATCH", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), ccf)
|
req, err := c.NewRequest(ctx, "PATCH", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), ccf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
54
webhooks.go
54
webhooks.go
|
@ -2,16 +2,18 @@ package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateWebhook - Subscribes your webhook listener to events.
|
// CreateWebhook - Subscribes your webhook listener to events.
|
||||||
// Endpoint: POST /v1/notifications/webhooks
|
// Endpoint: POST /v1/notifications/webhooks
|
||||||
func (c *Client) CreateWebhook(createWebhookRequest *CreateWebhookRequest) (*Webhook, error) {
|
func (c *Client) CreateWebhook(ctx context.Context, createWebhookRequest *CreateWebhookRequest) (*Webhook, error) {
|
||||||
req, err := c.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), createWebhookRequest)
|
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), createWebhookRequest)
|
||||||
webhook := &Webhook{}
|
webhook := &Webhook{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return webhook, err
|
return webhook, err
|
||||||
|
@ -23,8 +25,8 @@ func (c *Client) CreateWebhook(createWebhookRequest *CreateWebhookRequest) (*Web
|
||||||
|
|
||||||
// GetWebhook - Shows details for a webhook, by ID.
|
// GetWebhook - Shows details for a webhook, by ID.
|
||||||
// Endpoint: GET /v1/notifications/webhooks/ID
|
// Endpoint: GET /v1/notifications/webhooks/ID
|
||||||
func (c *Client) GetWebhook(webhookID string) (*Webhook, error) {
|
func (c *Client) GetWebhook(ctx context.Context, webhookID string) (*Webhook, error) {
|
||||||
req, err := c.NewRequest(http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/notifications/webhooks/", webhookID), nil)
|
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/notifications/webhooks/", webhookID), nil)
|
||||||
webhook := &Webhook{}
|
webhook := &Webhook{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return webhook, err
|
return webhook, err
|
||||||
|
@ -36,8 +38,8 @@ func (c *Client) GetWebhook(webhookID string) (*Webhook, error) {
|
||||||
|
|
||||||
// UpdateWebhook - Updates a webhook to replace webhook fields with new values.
|
// UpdateWebhook - Updates a webhook to replace webhook fields with new values.
|
||||||
// Endpoint: PATCH /v1/notifications/webhooks/ID
|
// Endpoint: PATCH /v1/notifications/webhooks/ID
|
||||||
func (c *Client) UpdateWebhook(webhookID string, fields []WebhookField) (*Webhook, error) {
|
func (c *Client) UpdateWebhook(ctx context.Context, webhookID string, fields []WebhookField) (*Webhook, error) {
|
||||||
req, err := c.NewRequest(http.MethodPatch, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), fields)
|
req, err := c.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), fields)
|
||||||
webhook := &Webhook{}
|
webhook := &Webhook{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return webhook, err
|
return webhook, err
|
||||||
|
@ -49,11 +51,11 @@ func (c *Client) UpdateWebhook(webhookID string, fields []WebhookField) (*Webhoo
|
||||||
|
|
||||||
// ListWebhooks - Lists webhooks for an app.
|
// ListWebhooks - Lists webhooks for an app.
|
||||||
// Endpoint: GET /v1/notifications/webhooks
|
// Endpoint: GET /v1/notifications/webhooks
|
||||||
func (c *Client) ListWebhooks(anchorType string) (*ListWebhookResponse, error) {
|
func (c *Client) ListWebhooks(ctx context.Context, anchorType string) (*ListWebhookResponse, error) {
|
||||||
if len(anchorType) == 0 {
|
if len(anchorType) == 0 {
|
||||||
anchorType = AncorTypeApplication
|
anchorType = AncorTypeApplication
|
||||||
}
|
}
|
||||||
req, err := c.NewRequest(http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), nil)
|
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), nil)
|
||||||
q := req.URL.Query()
|
q := req.URL.Query()
|
||||||
q.Add("anchor_type", anchorType)
|
q.Add("anchor_type", anchorType)
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
|
@ -68,8 +70,8 @@ func (c *Client) ListWebhooks(anchorType string) (*ListWebhookResponse, error) {
|
||||||
|
|
||||||
// DeleteWebhook - Deletes a webhook, by ID.
|
// DeleteWebhook - Deletes a webhook, by ID.
|
||||||
// Endpoint: DELETE /v1/notifications/webhooks/ID
|
// Endpoint: DELETE /v1/notifications/webhooks/ID
|
||||||
func (c *Client) DeleteWebhook(webhookID string) error {
|
func (c *Client) DeleteWebhook(ctx context.Context, webhookID string) error {
|
||||||
req, err := c.NewRequest(http.MethodDelete, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), nil)
|
req, err := c.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -80,7 +82,7 @@ func (c *Client) DeleteWebhook(webhookID string) error {
|
||||||
|
|
||||||
// VerifyWebhookSignature - Use this to verify the signature of a webhook recieved from paypal.
|
// VerifyWebhookSignature - Use this to verify the signature of a webhook recieved from paypal.
|
||||||
// Endpoint: POST /v1/notifications/verify-webhook-signature
|
// Endpoint: POST /v1/notifications/verify-webhook-signature
|
||||||
func (c *Client) VerifyWebhookSignature(httpReq *http.Request, webhookID string) (*VerifyWebhookResponse, error) {
|
func (c *Client) VerifyWebhookSignature(ctx context.Context, httpReq *http.Request, webhookID string) (*VerifyWebhookResponse, error) {
|
||||||
type verifyWebhookSignatureRequest struct {
|
type verifyWebhookSignatureRequest struct {
|
||||||
AuthAlgo string `json:"auth_algo,omitempty"`
|
AuthAlgo string `json:"auth_algo,omitempty"`
|
||||||
CertURL string `json:"cert_url,omitempty"`
|
CertURL string `json:"cert_url,omitempty"`
|
||||||
|
@ -88,16 +90,18 @@ func (c *Client) VerifyWebhookSignature(httpReq *http.Request, webhookID string)
|
||||||
TransmissionSig string `json:"transmission_sig,omitempty"`
|
TransmissionSig string `json:"transmission_sig,omitempty"`
|
||||||
TransmissionTime string `json:"transmission_time,omitempty"`
|
TransmissionTime string `json:"transmission_time,omitempty"`
|
||||||
WebhookID string `json:"webhook_id,omitempty"`
|
WebhookID string `json:"webhook_id,omitempty"`
|
||||||
WebhookEvent json.RawMessage `json:"webhook_event"`
|
Event json.RawMessage `json:"webhook_event,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the content
|
// Read the content
|
||||||
var bodyBytes []byte
|
var bodyBytes []byte
|
||||||
if httpReq.Body != nil {
|
if httpReq.Body != nil {
|
||||||
bodyBytes, _ = ioutil.ReadAll(httpReq.Body)
|
bodyBytes, _ = io.ReadAll(httpReq.Body)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("cannot verify webhook for HTTP Request with empty body")
|
||||||
}
|
}
|
||||||
// Restore the io.ReadCloser to its original state
|
// Restore the io.ReadCloser to its original state
|
||||||
httpReq.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
httpReq.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||||
|
|
||||||
verifyRequest := verifyWebhookSignatureRequest{
|
verifyRequest := verifyWebhookSignatureRequest{
|
||||||
AuthAlgo: httpReq.Header.Get("PAYPAL-AUTH-ALGO"),
|
AuthAlgo: httpReq.Header.Get("PAYPAL-AUTH-ALGO"),
|
||||||
|
@ -106,12 +110,12 @@ func (c *Client) VerifyWebhookSignature(httpReq *http.Request, webhookID string)
|
||||||
TransmissionSig: httpReq.Header.Get("PAYPAL-TRANSMISSION-SIG"),
|
TransmissionSig: httpReq.Header.Get("PAYPAL-TRANSMISSION-SIG"),
|
||||||
TransmissionTime: httpReq.Header.Get("PAYPAL-TRANSMISSION-TIME"),
|
TransmissionTime: httpReq.Header.Get("PAYPAL-TRANSMISSION-TIME"),
|
||||||
WebhookID: webhookID,
|
WebhookID: webhookID,
|
||||||
WebhookEvent: json.RawMessage(bodyBytes),
|
Event: json.RawMessage(bodyBytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
response := &VerifyWebhookResponse{}
|
response := &VerifyWebhookResponse{}
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/verify-webhook-signature"), verifyRequest)
|
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/verify-webhook-signature"), verifyRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -122,3 +126,19 @@ func (c *Client) VerifyWebhookSignature(httpReq *http.Request, webhookID string)
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetWebhookEventTypes - Lists all webhook event types.
|
||||||
|
// Endpoint: GET /v1/notifications/webhooks-event-types
|
||||||
|
func (c *Client) GetWebhookEventTypes(ctx context.Context) (*WebhookEventTypesResponse, error) {
|
||||||
|
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks-event-types"), nil)
|
||||||
|
q := req.URL.Query()
|
||||||
|
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
resp := &WebhookEventTypesResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -10,9 +11,9 @@ import (
|
||||||
// Allows for the customisation of the payment experience
|
// Allows for the customisation of the payment experience
|
||||||
//
|
//
|
||||||
// Endpoint: POST /v1/payment-experience/web-profiles
|
// Endpoint: POST /v1/payment-experience/web-profiles
|
||||||
func (c *Client) CreateWebProfile(wp WebProfile) (*WebProfile, error) {
|
func (c *Client) CreateWebProfile(ctx context.Context, wp WebProfile) (*WebProfile, error) {
|
||||||
url := fmt.Sprintf("%s%s", c.APIBase, "/v1/payment-experience/web-profiles")
|
url := fmt.Sprintf("%s%s", c.APIBase, "/v1/payment-experience/web-profiles")
|
||||||
req, err := c.NewRequest("POST", url, wp)
|
req, err := c.NewRequest(ctx, "POST", url, wp)
|
||||||
response := &WebProfile{}
|
response := &WebProfile{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -29,11 +30,11 @@ func (c *Client) CreateWebProfile(wp WebProfile) (*WebProfile, error) {
|
||||||
// GetWebProfile gets an exists payment experience from Paypal
|
// GetWebProfile gets an exists payment experience from Paypal
|
||||||
//
|
//
|
||||||
// Endpoint: GET /v1/payment-experience/web-profiles/<profile-id>
|
// Endpoint: GET /v1/payment-experience/web-profiles/<profile-id>
|
||||||
func (c *Client) GetWebProfile(profileID string) (*WebProfile, error) {
|
func (c *Client) GetWebProfile(ctx context.Context, profileID string) (*WebProfile, error) {
|
||||||
var wp WebProfile
|
var wp WebProfile
|
||||||
|
|
||||||
url := fmt.Sprintf("%s%s%s", c.APIBase, "/v1/payment-experience/web-profiles/", profileID)
|
url := fmt.Sprintf("%s%s%s", c.APIBase, "/v1/payment-experience/web-profiles/", profileID)
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &wp, err
|
return &wp, err
|
||||||
|
@ -50,14 +51,14 @@ func (c *Client) GetWebProfile(profileID string) (*WebProfile, error) {
|
||||||
return &wp, nil
|
return &wp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebProfiles retreieves web experience profiles from Paypal
|
// GetWebProfiles retrieves web experience profiles from Paypal
|
||||||
//
|
//
|
||||||
// Endpoint: GET /v1/payment-experience/web-profiles
|
// Endpoint: GET /v1/payment-experience/web-profiles
|
||||||
func (c *Client) GetWebProfiles() ([]WebProfile, error) {
|
func (c *Client) GetWebProfiles(ctx context.Context) ([]WebProfile, error) {
|
||||||
var wps []WebProfile
|
var wps []WebProfile
|
||||||
|
|
||||||
url := fmt.Sprintf("%s%s", c.APIBase, "/v1/payment-experience/web-profiles")
|
url := fmt.Sprintf("%s%s", c.APIBase, "/v1/payment-experience/web-profiles")
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wps, err
|
return wps, err
|
||||||
|
@ -73,7 +74,7 @@ func (c *Client) GetWebProfiles() ([]WebProfile, error) {
|
||||||
// SetWebProfile sets a web experience profile in Paypal with given id
|
// SetWebProfile sets a web experience profile in Paypal with given id
|
||||||
//
|
//
|
||||||
// Endpoint: PUT /v1/payment-experience/web-profiles
|
// Endpoint: PUT /v1/payment-experience/web-profiles
|
||||||
func (c *Client) SetWebProfile(wp WebProfile) error {
|
func (c *Client) SetWebProfile(ctx context.Context, wp WebProfile) error {
|
||||||
|
|
||||||
if wp.ID == "" {
|
if wp.ID == "" {
|
||||||
return fmt.Errorf("paypal: no ID specified for WebProfile")
|
return fmt.Errorf("paypal: no ID specified for WebProfile")
|
||||||
|
@ -81,7 +82,7 @@ func (c *Client) SetWebProfile(wp WebProfile) error {
|
||||||
|
|
||||||
url := fmt.Sprintf("%s%s%s", c.APIBase, "/v1/payment-experience/web-profiles/", wp.ID)
|
url := fmt.Sprintf("%s%s%s", c.APIBase, "/v1/payment-experience/web-profiles/", wp.ID)
|
||||||
|
|
||||||
req, err := c.NewRequest("PUT", url, wp)
|
req, err := c.NewRequest(ctx, "PUT", url, wp)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -97,11 +98,11 @@ func (c *Client) SetWebProfile(wp WebProfile) error {
|
||||||
// DeleteWebProfile deletes a web experience profile from Paypal with given id
|
// DeleteWebProfile deletes a web experience profile from Paypal with given id
|
||||||
//
|
//
|
||||||
// Endpoint: DELETE /v1/payment-experience/web-profiles
|
// Endpoint: DELETE /v1/payment-experience/web-profiles
|
||||||
func (c *Client) DeleteWebProfile(profileID string) error {
|
func (c *Client) DeleteWebProfile(ctx context.Context, profileID string) error {
|
||||||
|
|
||||||
url := fmt.Sprintf("%s%s%s", c.APIBase, "/v1/payment-experience/web-profiles/", profileID)
|
url := fmt.Sprintf("%s%s%s", c.APIBase, "/v1/payment-experience/web-profiles/", profileID)
|
||||||
|
|
||||||
req, err := c.NewRequest("DELETE", url, nil)
|
req, err := c.NewRequest(ctx, "DELETE", url, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in New Issue
Block a user