forked from go-packages/paypal
Compare commits
1 Commits
master
...
revert-121
Author | SHA1 | Date | |
---|---|---|---|
|
ab4c40e224 |
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,4 +0,0 @@
|
||||||
#### 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
50
.github/workflows/lint-test.yaml
vendored
|
@ -1,50 +0,0 @@
|
||||||
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,3 +1,2 @@
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
vendor/
|
|
8
.travis.yml
Normal file
8
.travis.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.11
|
||||||
|
- 1.12
|
||||||
|
install:
|
||||||
|
- export PATH=$PATH:$HOME/gopath/bin
|
||||||
|
script:
|
||||||
|
- go test -v -race
|
|
@ -1,10 +0,0 @@
|
||||||
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) 2024 Aliaksandr Pliutau
|
Copyright (c) 2019 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
|
||||||
|
|
142
README.md
142
README.md
|
@ -1,31 +1,74 @@
|
||||||
[Docs](https://pkg.go.dev/github.com/plutov/paypal)
|
[![Go Report Card](https://goreportcard.com/badge/plutov/paypal)](https://goreportcard.com/report/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)
|
||||||
|
|
||||||
<p>
|
### Go client for PayPal REST API
|
||||||
<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>
|
|
||||||
|
|
||||||
# Go client for PayPal REST API
|
Currently supports **v2** only, if you want to use **v1**, use **v1.1.4** git tag.
|
||||||
|
|
||||||
## Paypal REST API Docs
|
### Coverage
|
||||||
|
|
||||||
[Get started with PayPal REST APIs](https://developer.paypal.com/api/rest/)
|
* POST /v1/oauth2/token
|
||||||
|
* 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**
|
||||||
|
|
||||||
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`
|
### New Client
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/plutov/paypal/v4"
|
import "github.com/plutov/paypal"
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
@ -49,6 +92,21 @@ 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
|
||||||
|
@ -64,11 +122,7 @@ order, err := c.GetOrder("O-4J082351X3132253H")
|
||||||
### Create an Order
|
### Create an Order
|
||||||
|
|
||||||
```go
|
```go
|
||||||
ctx := context.Background()
|
order, err := c.CreateOrder(paypal.OrderIntentCapture, []paypal.PurchaseUnitRequest{paypal.PurchaseUnitRequest{ReferenceID: "ref-id", Amount: paypal.Amount{Total: "7.00", Currency: "USD"}}})
|
||||||
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
|
||||||
|
@ -124,7 +178,7 @@ payout := paypal.Payout{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
payoutResp, err := c.CreatePayout(payout)
|
payoutResp, err := c.CreateSinglePayout(payout)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get payout by ID
|
### Get payout by ID
|
||||||
|
@ -186,6 +240,7 @@ 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! ",
|
||||||
|
@ -233,7 +288,6 @@ c.GetCreditCards(nil)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Webhooks
|
### Webhooks
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Create a webhook
|
// Create a webhook
|
||||||
c.CreateWebhook(paypal.CreateWebhookRequest{
|
c.CreateWebhook(paypal.CreateWebhookRequest{
|
||||||
|
@ -266,46 +320,22 @@ c.DeleteWebhook("WebhookID")
|
||||||
|
|
||||||
// List registered webhooks
|
// List registered webhooks
|
||||||
c.ListWebhooks(paypal.AncorTypeApplication)
|
c.ListWebhooks(paypal.AncorTypeApplication)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Generate Next Invoice Number
|
### How to Contribute
|
||||||
|
|
||||||
```go
|
* Fork a repository
|
||||||
// GenerateInvoiceNumber: generates the next invoice number that is available to the merchant.
|
* Add/Fix something
|
||||||
c.GenerateInvoiceNumber(ctx) // might return something like "0001" or "0010".
|
* Check that tests are passing
|
||||||
```
|
* Create PR
|
||||||
|
|
||||||
### Get Invoice Details by ID
|
Current contributors:
|
||||||
|
|
||||||
```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:
|
|
||||||
|
|
||||||
- [Alex Pliutau](https://github.com/plutov)
|
|
||||||
- [Roopak Venkatakrishnan](https://github.com/roopakv)
|
- [Roopak Venkatakrishnan](https://github.com/roopakv)
|
||||||
|
- [Alex Pliutau](https://github.com/plutov)
|
||||||
|
|
||||||
## Tests
|
### Tests
|
||||||
|
|
||||||
```
|
* Unit tests: `go test -v ./...`
|
||||||
go test -v ./...
|
* Integration tests: `go test -tags=integration`
|
||||||
```
|
|
||||||
|
|
|
@ -2,16 +2,15 @@ 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(ctx context.Context, authID string) (*Authorization, error) {
|
func (c *Client) GetAuthorization(authID string) (*Authorization, error) {
|
||||||
buf := bytes.NewBuffer([]byte(""))
|
buf := bytes.NewBuffer([]byte(""))
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/payments/authorizations/", authID), buf)
|
req, err := http.NewRequest("GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/payments/authorizations/", authID), buf)
|
||||||
auth := &Authorization{}
|
auth := &Authorization{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -25,19 +24,19 @@ func (c *Client) GetAuthorization(ctx context.Context, authID string) (*Authoriz
|
||||||
// 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(ctx context.Context, authID string, paymentCaptureRequest *PaymentCaptureRequest) (*PaymentCaptureResponse, error) {
|
func (c *Client) CaptureAuthorization(authID string, paymentCaptureRequest *PaymentCaptureRequest) (*PaymentCaptureResponse, error) {
|
||||||
return c.CaptureAuthorizationWithPaypalRequestId(ctx, authID, paymentCaptureRequest, "")
|
return c.CaptureAuthorizationWithPaypalRequestId(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(ctx context.Context,
|
func (c *Client) CaptureAuthorizationWithPaypalRequestId(
|
||||||
authID string,
|
authID string,
|
||||||
paymentCaptureRequest *PaymentCaptureRequest,
|
paymentCaptureRequest *PaymentCaptureRequest,
|
||||||
requestID string,
|
requestID string,
|
||||||
) (*PaymentCaptureResponse, error) {
|
) (*PaymentCaptureResponse, error) {
|
||||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/capture"), paymentCaptureRequest)
|
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/capture"), paymentCaptureRequest)
|
||||||
paymentCaptureResponse := &PaymentCaptureResponse{}
|
paymentCaptureResponse := &PaymentCaptureResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,9 +53,9 @@ func (c *Client) CaptureAuthorizationWithPaypalRequestId(ctx context.Context,
|
||||||
|
|
||||||
// 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(ctx context.Context, authID string) (*Authorization, error) {
|
func (c *Client) VoidAuthorization(authID string) (*Authorization, error) {
|
||||||
buf := bytes.NewBuffer([]byte(""))
|
buf := bytes.NewBuffer([]byte(""))
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/void"), buf)
|
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/void"), buf)
|
||||||
auth := &Authorization{}
|
auth := &Authorization{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -68,11 +67,11 @@ func (c *Client) VoidAuthorization(ctx context.Context, authID string) (*Authori
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReauthorizeAuthorization reauthorize a Paypal account payment.
|
// ReauthorizeAuthorization reauthorize a Paypal account payment.
|
||||||
// PayPal recommends reauthorizing payment after ~3 days
|
// PayPal recommends to reauthorize payment after ~3 days
|
||||||
// Endpoint: POST /v2/payments/authorizations/ID/reauthorize
|
// Endpoint: POST /v2/payments/authorizations/ID/reauthorize
|
||||||
func (c *Client) ReauthorizeAuthorization(ctx context.Context, authID string, a *Amount) (*Authorization, error) {
|
func (c *Client) ReauthorizeAuthorization(authID string, a *Amount) (*Authorization, error) {
|
||||||
buf := bytes.NewBuffer([]byte(`{"amount":{"currency_code":"` + a.Currency + `","value":"` + a.Total + `"}}`))
|
buf := bytes.NewBuffer([]byte(`{"amount":{"currency":"` + a.Currency + `","total":"` + a.Total + `"}}`))
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/reauthorize"), buf)
|
req, err := http.NewRequest("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
Normal file
133
billing.go
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
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
|
||||||
|
}
|
76
client.go
76
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(ctx context.Context) (*TokenResponse, error) {
|
func (c *Client) GetAccessToken() (*TokenResponse, error) {
|
||||||
buf := bytes.NewBuffer([]byte("grant_type=client_credentials"))
|
buf := bytes.NewBuffer([]byte("grant_type=client_credentials"))
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/oauth2/token"), buf)
|
req, err := http.NewRequest("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
|
||||||
// unmarshalled into v, or if v is an io.Writer, the response will
|
// unmarshaled 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,36 +97,21 @@ 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 = io.ReadAll(resp.Body)
|
data, err = ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
if err == nil && len(data) > 0 {
|
if err == nil && len(data) > 0 {
|
||||||
err := json.Unmarshal(data, errResp)
|
json.Unmarshal(data, errResp)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return errResp
|
return errResp
|
||||||
|
@ -136,8 +121,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 {
|
||||||
_, err := io.Copy(w, resp.Body)
|
io.Copy(w, resp.Body)
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.NewDecoder(resp.Body).Decode(v)
|
return json.NewDecoder(resp.Body).Decode(v)
|
||||||
|
@ -148,25 +133,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 || (!c.tokenExpiresAt.IsZero() && time.Until(c.tokenExpiresAt) < RequestNewTokenBeforeExpiresIn) {
|
if c.Token != nil {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +164,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(ctx context.Context, method, url string, payload interface{}) (*http.Request, error) {
|
func (c *Client) NewRequest(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)
|
||||||
|
@ -188,5 +173,24 @@ func (c *Client) NewRequest(ctx context.Context, method, url string, payload int
|
||||||
}
|
}
|
||||||
buf = bytes.NewBuffer(b)
|
buf = bytes.NewBuffer(b)
|
||||||
}
|
}
|
||||||
return http.NewRequestWithContext(ctx, method, url, buf)
|
return http.NewRequest(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))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
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
118
const.go
|
@ -1,118 +0,0 @@
|
||||||
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"
|
|
||||||
)
|
|
17
example_test.go
Normal file
17
example_test.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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,11 +1,3 @@
|
||||||
module euphoria-laxis.fr/go-packages/paypale/v4
|
module github.com/plutov/paypal/v3
|
||||||
|
|
||||||
go 1.23
|
go 1.12
|
||||||
|
|
||||||
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
|
@ -1,10 +0,0 @@
|
||||||
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,17 +1,65 @@
|
||||||
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>
|
||||||
func (c *Client) GetUserInfo(ctx context.Context, schema string) (*UserInfo, error) {
|
// Pass the schema that is used to return as per openidconnect protocol. The only supported schema value is openid.
|
||||||
|
func (c *Client) GetUserInfo(schema string) (*UserInfo, error) {
|
||||||
u := &UserInfo{}
|
u := &UserInfo{}
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v1/identity/openidconnect/userinfo/?schema=", schema), nil)
|
req, err := http.NewRequest("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
|
||||||
}
|
}
|
||||||
|
|
214
integration_test.go
Normal file
214
integration_test.go
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
// +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
38
invoicing.go
|
@ -1,38 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,406 +0,0 @@
|
||||||
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,17 +1,13 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"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(ctx context.Context, orderID string) (*Order, error) {
|
func (c *Client) GetOrder(orderID string) (*Order, error) {
|
||||||
order := &Order{}
|
order := &Order{}
|
||||||
|
|
||||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/checkout/orders/", orderID), nil)
|
req, err := c.NewRequest("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
|
||||||
}
|
}
|
||||||
|
@ -23,39 +19,23 @@ func (c *Client) GetOrder(ctx context.Context, orderID string) (*Order, error) {
|
||||||
return order, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an order
|
// CreateOrder - Use this call to create an order
|
||||||
// Endpoint: POST /v2/checkout/orders
|
// Endpoint: POST /v2/checkout/orders
|
||||||
func (c *Client) CreateOrder(ctx context.Context, intent string, purchaseUnits []PurchaseUnitRequest, paymentSource *PaymentSource, appContext *ApplicationContext) (*Order, error) {
|
func (c *Client) CreateOrder(intent string, purchaseUnits []PurchaseUnitRequest, payer *CreateOrderPayer, 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"`
|
||||||
PaymentSource *PaymentSource `json:"payment_source,omitempty"`
|
Payer *CreateOrderPayer `json:"payer,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(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders"), createOrderRequest{Intent: intent, PurchaseUnits: purchaseUnits, PaymentSource: paymentSource, ApplicationContext: appContext})
|
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders"), createOrderRequest{Intent: intent, PurchaseUnits: purchaseUnits, Payer: payer, 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
|
||||||
}
|
}
|
||||||
|
@ -65,37 +45,27 @@ func (c *Client) CreateOrderWithPaypalRequestID(ctx context.Context,
|
||||||
|
|
||||||
// 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(ctx context.Context, orderID string, op string, path string, value map[string]string) error {
|
func (c *Client) UpdateOrder(orderID string, purchaseUnits []PurchaseUnitRequest) (*Order, error) {
|
||||||
|
order := &Order{}
|
||||||
|
|
||||||
type patchRequest struct {
|
req, err := c.NewRequest("PATCH", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/checkout/orders/", orderID), purchaseUnits)
|
||||||
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 err
|
return order, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = c.SendWithAuth(req, nil); err != nil {
|
if err = c.SendWithAuth(req, order); err != nil {
|
||||||
return err
|
return order, 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(ctx context.Context, orderID string, authorizeOrderRequest AuthorizeOrderRequest) (*AuthorizeOrderResponse, error) {
|
func (c *Client) AuthorizeOrder(orderID string, authorizeOrderRequest AuthorizeOrderRequest) (*Authorization, error) {
|
||||||
auth := &AuthorizeOrderResponse{}
|
auth := &Authorization{}
|
||||||
|
|
||||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders/"+orderID+"/authorize"), authorizeOrderRequest)
|
req, err := c.NewRequest("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
|
||||||
}
|
}
|
||||||
|
@ -109,23 +79,22 @@ func (c *Client) AuthorizeOrder(ctx context.Context, orderID string, authorizeOr
|
||||||
|
|
||||||
// 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(ctx context.Context, orderID string, captureOrderRequest CaptureOrderRequest) (*CaptureOrderResponse, error) {
|
func (c *Client) CaptureOrder(orderID string, captureOrderRequest CaptureOrderRequest) (*CaptureOrderResponse, error) {
|
||||||
return c.CaptureOrderWithPaypalRequestId(ctx, orderID, captureOrderRequest, "", nil)
|
return c.CaptureOrderWithPaypalRequestId(orderID, captureOrderRequest, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(ctx context.Context,
|
func (c *Client) CaptureOrderWithPaypalRequestId(
|
||||||
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(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/checkout/orders/"+orderID+"/capture"), captureOrderRequest)
|
req, err := c.NewRequest("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
|
||||||
}
|
}
|
||||||
|
@ -134,15 +103,6 @@ func (c *Client) CaptureOrderWithPaypalRequestId(ctx context.Context,
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -152,20 +112,20 @@ func (c *Client) CaptureOrderWithPaypalRequestId(ctx context.Context,
|
||||||
|
|
||||||
// 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(ctx context.Context, captureID string, refundCaptureRequest RefundCaptureRequest) (*RefundResponse, error) {
|
func (c *Client) RefundCapture(captureID string, refundCaptureRequest RefundCaptureRequest) (*RefundResponse, error) {
|
||||||
return c.RefundCaptureWithPaypalRequestId(ctx, captureID, refundCaptureRequest, "")
|
return c.RefundCaptureWithPaypalRequestId(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(ctx context.Context,
|
func (c *Client) RefundCaptureWithPaypalRequestId(
|
||||||
captureID string,
|
captureID string,
|
||||||
refundCaptureRequest RefundCaptureRequest,
|
refundCaptureRequest RefundCaptureRequest,
|
||||||
requestID string,
|
requestID string,
|
||||||
) (*RefundResponse, error) {
|
) (*RefundResponse, error) {
|
||||||
refund := &RefundResponse{}
|
refund := &RefundResponse{}
|
||||||
|
|
||||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/captures/"+captureID+"/refund"), refundCaptureRequest)
|
req, err := c.NewRequest("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
|
||||||
}
|
}
|
||||||
|
@ -179,19 +139,3 @@ func (c *Client) RefundCaptureWithPaypalRequestId(ctx context.Context,
|
||||||
}
|
}
|
||||||
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,15 +1,14 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreatePayout submits a payout with an asynchronous API call, which immediately returns the results of a PayPal payment.
|
// CreateSinglePayout 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) CreatePayout(ctx context.Context, p Payout) (*PayoutResponse, error) {
|
func (c *Client) CreateSinglePayout(p Payout) (*PayoutResponse, error) {
|
||||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts"), p)
|
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts"), p)
|
||||||
response := &PayoutResponse{}
|
response := &PayoutResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -26,8 +25,8 @@ func (c *Client) CreatePayout(ctx context.Context, p Payout) (*PayoutResponse, e
|
||||||
// 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(ctx context.Context, payoutBatchID string) (*PayoutResponse, error) {
|
func (c *Client) GetPayout(payoutBatchID string) (*PayoutResponse, error) {
|
||||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts/"+payoutBatchID), nil)
|
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts/"+payoutBatchID), nil)
|
||||||
response := &PayoutResponse{}
|
response := &PayoutResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,8 +43,8 @@ func (c *Client) GetPayout(ctx context.Context, payoutBatchID string) (*PayoutRe
|
||||||
// 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(ctx context.Context, payoutItemID string) (*PayoutItemResponse, error) {
|
func (c *Client) GetPayoutItem(payoutItemID string) (*PayoutItemResponse, error) {
|
||||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID), nil)
|
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID), nil)
|
||||||
response := &PayoutItemResponse{}
|
response := &PayoutItemResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -62,8 +61,8 @@ func (c *Client) GetPayoutItem(ctx context.Context, payoutItemID string) (*Payou
|
||||||
// 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(ctx context.Context, payoutItemID string) (*PayoutItemResponse, error) {
|
func (c *Client) CancelPayoutItem(payoutItemID string) (*PayoutItemResponse, error) {
|
||||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID+"/cancel"), nil)
|
req, err := c.NewRequest("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
119
products.go
|
@ -1,119 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
56
sale.go
56
sale.go
|
@ -1,17 +1,55 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"context"
|
|
||||||
"fmt"
|
// GetSale returns a sale by ID
|
||||||
)
|
// 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.
|
||||||
|
// Endpoint: POST /v1/payments/sale/ID/refund
|
||||||
|
func (c *Client) RefundSale(saleID string, a *Amount) (*Refund, error) {
|
||||||
|
type refundRequest struct {
|
||||||
|
Amount *Amount `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
// GetRefund by ID
|
|
||||||
// Use it to look up details of a specific refund on direct and captured payments.
|
|
||||||
// Endpoint: GET /v2/payments/refund/ID
|
|
||||||
func (c *Client) GetRefund(ctx context.Context, refundID string) (*Refund, error) {
|
|
||||||
refund := &Refund{}
|
refund := &Refund{}
|
||||||
|
|
||||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/refund/"+refundID), nil)
|
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/sale/"+saleID+"/refund"), &refundRequest{Amount: a})
|
||||||
|
if err != nil {
|
||||||
|
return refund, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.SendWithAuth(req, refund); err != nil {
|
||||||
|
return refund, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return refund, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRefund by ID
|
||||||
|
// Use it to look up details of a specific refund on direct and captured payments.
|
||||||
|
// Endpoint: GET /v2/payments/refund/ID
|
||||||
|
func (c *Client) GetRefund(refundID string) (*Refund, error) {
|
||||||
|
refund := &Refund{}
|
||||||
|
|
||||||
|
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/refund/"+refundID), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return refund, err
|
return refund, err
|
||||||
}
|
}
|
||||||
|
|
225
subscription.go
225
subscription.go
|
@ -1,149 +1,33 @@
|
||||||
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 {
|
||||||
SubscriptionBase
|
ID string `json:"id,omitempty"`
|
||||||
SubscriptionDetails
|
PlanID string `json:"plan_id,omitempty"`
|
||||||
BillingInfo BillingInfo `json:"billing_info,omitempty"` // not found in documentation
|
StartTime time.Time `json:"start_time,omitempty"`
|
||||||
SharedResponse
|
Quantity string `json:"quantity,omitempty"`
|
||||||
}
|
ShippingAmount ShippingAmount `json:"shipping_amount,omitempty"`
|
||||||
|
Subscriber Subscriber `json:"subscriber,omitempty"`
|
||||||
SubscriptionCaptureResponse struct {
|
BillingInfo BillingInfo `json:"billing_info,omitempty"`
|
||||||
Status SubscriptionTransactionStatus `json:"status"`
|
CreateTime time.Time `json:"create_time,omitempty"`
|
||||||
Id string `json:"id"`
|
UpdateTime time.Time `json:"update_time,omitempty"`
|
||||||
AmountWithBreakdown AmountWithBreakdown `json:"amount_with_breakdown"`
|
Links []Link `json:"links,omitempty"`
|
||||||
PayerName Name `json:"payer_name"`
|
Status string `json:"status,omitempty"`
|
||||||
PayerEmail string `json:"payer_email"`
|
StatusUpdateTime time.Time `json:"status_update_time,omitempty"`
|
||||||
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"`
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Subscription) GetUpdatePatch() []Patch {
|
|
||||||
result := []Patch{
|
|
||||||
{
|
|
||||||
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{}
|
|
||||||
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/#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.
|
// GetSubscriptionDetails shows details for a subscription, by ID.
|
||||||
// Endpoint: GET /v1/billing/subscriptions/
|
// Endpoint: GET /v1/billing/subscriptions/
|
||||||
func (c *Client) GetSubscriptionDetails(ctx context.Context, subscriptionID string) (*SubscriptionDetailResp, error) {
|
func (c *Client) GetSubscriptionDetails(subscriptionID string) (*SubscriptionDetailResp, error) {
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
||||||
response := &SubscriptionDetailResp{}
|
response := &SubscriptionDetailResp{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
|
@ -151,84 +35,3 @@ func (c *Client) GetSubscriptionDetails(ctx context.Context, subscriptionID stri
|
||||||
err = c.SendWithAuth(req, response)
|
err = c.SendWithAuth(req, response)
|
||||||
return response, err
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,233 +0,0 @@
|
||||||
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,7 +1,6 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -31,15 +30,17 @@ 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"`
|
||||||
SharedListResponse
|
TotalItems int `json:"total_items"`
|
||||||
|
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(ctx context.Context, req *TransactionSearchRequest) (*TransactionSearchResponse, error) {
|
func (c *Client) ListTransactions(req *TransactionSearchRequest) (*TransactionSearchResponse, error) {
|
||||||
response := &TransactionSearchResponse{}
|
response := &TransactionSearchResponse{}
|
||||||
|
|
||||||
r, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/reporting/transactions"), nil)
|
r, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/reporting/transactions"), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
539
types.go
539
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-m.sandbox.paypal.com"
|
APIBaseSandBox = "https://api.sandbox.paypal.com"
|
||||||
|
|
||||||
// APIBaseLive points to the live version of the API
|
// APIBaseLive points to the live version of the API
|
||||||
APIBaseLive = "https://api-m.paypal.com"
|
APIBaseLive = "https://api.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,9 +85,13 @@ 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"
|
||||||
|
@ -116,31 +120,6 @@ 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"
|
||||||
|
@ -157,10 +136,10 @@ type (
|
||||||
|
|
||||||
// Address struct
|
// Address struct
|
||||||
Address struct {
|
Address struct {
|
||||||
Line1 string `json:"line1,omitempty"`
|
Line1 string `json:"line1"`
|
||||||
Line2 string `json:"line2,omitempty"`
|
Line2 string `json:"line2,omitempty"`
|
||||||
City string `json:"city,omitempty"`
|
City string `json:"city"`
|
||||||
CountryCode string `json:"country_code,omitempty"`
|
CountryCode string `json:"country_code"`
|
||||||
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"`
|
||||||
|
@ -192,282 +171,16 @@ 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"`
|
||||||
ShippingPreference ShippingPreference `json:"shipping_preference,omitempty"`
|
|
||||||
UserAction UserAction `json:"user_action,omitempty"`
|
|
||||||
PaymentMethod PaymentMethod `json:"payment_method,omitempty"`
|
|
||||||
LandingPage string `json:"landing_page,omitempty"`
|
LandingPage string `json:"landing_page,omitempty"`
|
||||||
|
ShippingPreference string `json:"shipping_preference,omitempty"`
|
||||||
|
UserAction string `json:"user_action,omitempty"`
|
||||||
ReturnURL string `json:"return_url,omitempty"`
|
ReturnURL string `json:"return_url,omitempty"`
|
||||||
CancelURL string `json:"cancel_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
|
||||||
Authorization struct {
|
Authorization struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
|
@ -483,13 +196,14 @@ 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 []PurchaseUnit `json:"purchase_units,omitempty"`
|
PurchaseUnits []PurchaseUnitRequest `json:"purchase_units,omitempty"`
|
||||||
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,33 +255,11 @@ 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"`
|
||||||
|
@ -586,14 +278,15 @@ type (
|
||||||
SenderBatchHeader *SenderBatchHeader `json:"sender_batch_header,omitempty"`
|
SenderBatchHeader *SenderBatchHeader `json:"sender_batch_header,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plan struct
|
// BillingAgreement struct
|
||||||
Plan struct {
|
BillingAgreement struct {
|
||||||
ID string `json:"id"`
|
Name string `json:"name,omitempty"`
|
||||||
Name string `json:"name"`
|
Description string `json:"description,omitempty"`
|
||||||
Description string `json:"description"`
|
StartDate JSONTime `json:"start_date,omitempty"`
|
||||||
CreateTime string `json:"create_time,omitempty"`
|
Plan BillingPlan `json:"plan,omitempty"`
|
||||||
UpdateTime string `json:"update_time,omitempty"`
|
Payer Payer `json:"payer,omitempty"`
|
||||||
PaymentDefinitions []PaymentDefinition `json:"payment_definitions,omitempty"`
|
ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"`
|
||||||
|
OverrideMerchantPreferences *MerchantPreferences `json:"override_merchant_preferences,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BillingInfo struct
|
// BillingInfo struct
|
||||||
|
@ -617,14 +310,13 @@ 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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,8 +328,7 @@ 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
|
||||||
|
@ -668,7 +359,9 @@ 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"`
|
||||||
SharedListResponse
|
Links []Link `json:"links"`
|
||||||
|
TotalItems int `json:"total_items"`
|
||||||
|
TotalPages int `json:"total_pages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreditCardToken struct
|
// CreditCardToken struct
|
||||||
|
@ -729,9 +422,6 @@ type (
|
||||||
ErrorResponseDetail struct {
|
ErrorResponseDetail struct {
|
||||||
Field string `json:"field"`
|
Field string `json:"field"`
|
||||||
Issue string `json:"issue"`
|
Issue string `json:"issue"`
|
||||||
Name string `json:"name"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Links []Link `json:"link"`
|
Links []Link `json:"link"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,19 +515,10 @@ 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.
|
||||||
|
@ -885,7 +566,6 @@ type (
|
||||||
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
|
||||||
|
@ -903,46 +583,21 @@ type (
|
||||||
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"`
|
||||||
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
|
||||||
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
||||||
Links []Link `json:"links,omitempty"`
|
Links []Link `json:"links,omitempty"`
|
||||||
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
CustomID string `json:"custom_id,omitempty"`
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
Amount *PurchaseUnitAmount `json:"amount,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 {
|
||||||
Autthorizations []Authorization `json:"authorizations,omitempty"`
|
|
||||||
Captures []CaptureAmount `json:"captures,omitempty"`
|
Captures []CaptureAmount `json:"captures,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,9 +627,6 @@ type (
|
||||||
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
|
||||||
|
@ -982,7 +634,6 @@ 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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1050,44 +701,27 @@ type (
|
||||||
|
|
||||||
// PaymentSource structure
|
// PaymentSource structure
|
||||||
PaymentSource struct {
|
PaymentSource struct {
|
||||||
Card *PaymentSourceCard `json:"card,omitempty"`
|
Card *PaymentSourceCard `json:"card"`
|
||||||
Token *PaymentSourceToken `json:"token,omitempty"`
|
Token *PaymentSourceToken `json:"token"`
|
||||||
Paypal *PaymentSourcePaypal `json:"paypal,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaymentSourceCard structure
|
// PaymentSourceCard structure
|
||||||
PaymentSourceCard struct {
|
PaymentSourceCard struct {
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name"`
|
||||||
Number string `json:"number,omitempty"`
|
Number string `json:"number"`
|
||||||
Expiry string `json:"expiry,omitempty"`
|
Expiry string `json:"expiry"`
|
||||||
SecurityCode string `json:"security_code,omitempty"`
|
SecurityCode string `json:"security_code"`
|
||||||
LastDigits string `json:"last_digits,omitempty"`
|
LastDigits string `json:"last_digits"`
|
||||||
CardType string `json:"card_type,omitempty"`
|
CardType string `json:"card_type"`
|
||||||
BillingAddress *CardBillingAddress `json:"billing_address,omitempty"`
|
BillingAddress *CardBillingAddress `json:"billing_address"`
|
||||||
}
|
|
||||||
|
|
||||||
// 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,omitempty"`
|
AddressLine2 string `json:"address_line_2"`
|
||||||
AdminArea2 string `json:"admin_area_2,omitempty"`
|
AdminArea2 string `json:"admin_area_2"`
|
||||||
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"`
|
||||||
|
@ -1108,7 +742,6 @@ type (
|
||||||
// PayoutItem struct
|
// PayoutItem struct
|
||||||
PayoutItem struct {
|
PayoutItem struct {
|
||||||
RecipientType string `json:"recipient_type"`
|
RecipientType string `json:"recipient_type"`
|
||||||
RecipientWallet string `json:"recipient_wallet"`
|
|
||||||
Receiver string `json:"receiver"`
|
Receiver string `json:"receiver"`
|
||||||
Amount *AmountPayout `json:"amount"`
|
Amount *AmountPayout `json:"amount"`
|
||||||
Note string `json:"note,omitempty"`
|
Note string `json:"note,omitempty"`
|
||||||
|
@ -1150,7 +783,6 @@ type (
|
||||||
CaptureID string `json:"capture_id,omitempty"`
|
CaptureID string `json:"capture_id,omitempty"`
|
||||||
ParentPayment string `json:"parent_payment,omitempty"`
|
ParentPayment string `json:"parent_payment,omitempty"`
|
||||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||||
SaleID string `json:"sale_id,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefundResponse .
|
// RefundResponse .
|
||||||
|
@ -1158,7 +790,6 @@ 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
|
||||||
|
@ -1198,7 +829,8 @@ type (
|
||||||
|
|
||||||
//ShippingAmount struct
|
//ShippingAmount struct
|
||||||
ShippingAmount struct {
|
ShippingAmount struct {
|
||||||
Money
|
CurrencyCode string `json:"currency_code,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShippingAddress struct
|
// ShippingAddress struct
|
||||||
|
@ -1225,14 +857,8 @@ 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
|
||||||
|
@ -1243,7 +869,6 @@ 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"`
|
||||||
|
@ -1350,11 +975,7 @@ type (
|
||||||
VerificationStatus string `json:"verification_status,omitempty"`
|
VerificationStatus string `json:"verification_status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
WebhookEventTypesResponse struct {
|
// Webhook strunct
|
||||||
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"`
|
||||||
|
@ -1362,31 +983,23 @@ type (
|
||||||
Links []Link `json:"links"`
|
Links []Link `json:"links"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event struct.
|
// WebhookEvent struct
|
||||||
//
|
WebhookEvent 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
|
||||||
|
@ -1405,6 +1018,26 @@ 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"`
|
||||||
|
@ -1420,10 +1053,6 @@ 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"`
|
||||||
|
@ -1555,30 +1184,6 @@ 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
|
||||||
|
@ -1613,9 +1218,3 @@ 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,9 +2,16 @@ 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 {
|
||||||
|
@ -133,35 +140,6 @@ 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":{
|
||||||
|
@ -282,98 +260,6 @@ 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",
|
||||||
|
@ -471,3 +357,389 @@ 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,14 +1,13 @@
|
||||||
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(ctx context.Context, cc CreditCard) (*CreditCard, error) {
|
func (c *Client) StoreCreditCard(cc CreditCard) (*CreditCard, error) {
|
||||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/vault/credit-cards"), cc)
|
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/vault/credit-cards"), cc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -24,8 +23,8 @@ func (c *Client) StoreCreditCard(ctx context.Context, cc CreditCard) (*CreditCar
|
||||||
|
|
||||||
// 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(ctx context.Context, id string) error {
|
func (c *Client) DeleteCreditCard(id string) error {
|
||||||
req, err := c.NewRequest(ctx, "DELETE", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
req, err := c.NewRequest("DELETE", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -39,8 +38,8 @@ func (c *Client) DeleteCreditCard(ctx context.Context, 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(ctx context.Context, id string) (*CreditCard, error) {
|
func (c *Client) GetCreditCard(id string) (*CreditCard, error) {
|
||||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
req, err := c.NewRequest("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
|
||||||
}
|
}
|
||||||
|
@ -56,7 +55,7 @@ func (c *Client) GetCreditCard(ctx context.Context, id string) (*CreditCard, err
|
||||||
|
|
||||||
// GetCreditCards func
|
// GetCreditCards func
|
||||||
// Endpoint: GET /v1/vault/credit-cards
|
// Endpoint: GET /v1/vault/credit-cards
|
||||||
func (c *Client) GetCreditCards(ctx context.Context, ccf *CreditCardsFilter) (*CreditCards, error) {
|
func (c *Client) GetCreditCards(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
|
||||||
|
@ -66,7 +65,7 @@ func (c *Client) GetCreditCards(ctx context.Context, ccf *CreditCardsFilter) (*C
|
||||||
pageSize = ccf.PageSize
|
pageSize = ccf.PageSize
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s/v1/vault/credit-cards?page=%d&page_size=%d", c.APIBase, page, pageSize), nil)
|
req, err := c.NewRequest("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
|
||||||
}
|
}
|
||||||
|
@ -82,8 +81,8 @@ func (c *Client) GetCreditCards(ctx context.Context, ccf *CreditCardsFilter) (*C
|
||||||
|
|
||||||
// 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(ctx context.Context, id string, ccf []CreditCardField) (*CreditCard, error) {
|
func (c *Client) PatchCreditCard(id string, ccf []CreditCardField) (*CreditCard, error) {
|
||||||
req, err := c.NewRequest(ctx, "PATCH", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), ccf)
|
req, err := c.NewRequest("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,18 +2,16 @@ package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io/ioutil"
|
||||||
"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(ctx context.Context, createWebhookRequest *CreateWebhookRequest) (*Webhook, error) {
|
func (c *Client) CreateWebhook(createWebhookRequest *CreateWebhookRequest) (*Webhook, error) {
|
||||||
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), createWebhookRequest)
|
req, err := c.NewRequest(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
|
||||||
|
@ -25,8 +23,8 @@ func (c *Client) CreateWebhook(ctx context.Context, createWebhookRequest *Create
|
||||||
|
|
||||||
// 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(ctx context.Context, webhookID string) (*Webhook, error) {
|
func (c *Client) GetWebhook(webhookID string) (*Webhook, error) {
|
||||||
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/notifications/webhooks/", webhookID), nil)
|
req, err := c.NewRequest(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
|
||||||
|
@ -38,8 +36,8 @@ func (c *Client) GetWebhook(ctx context.Context, webhookID string) (*Webhook, er
|
||||||
|
|
||||||
// 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(ctx context.Context, webhookID string, fields []WebhookField) (*Webhook, error) {
|
func (c *Client) UpdateWebhook(webhookID string, fields []WebhookField) (*Webhook, error) {
|
||||||
req, err := c.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), fields)
|
req, err := c.NewRequest(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
|
||||||
|
@ -51,11 +49,11 @@ func (c *Client) UpdateWebhook(ctx context.Context, webhookID string, fields []W
|
||||||
|
|
||||||
// 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(ctx context.Context, anchorType string) (*ListWebhookResponse, error) {
|
func (c *Client) ListWebhooks(anchorType string) (*ListWebhookResponse, error) {
|
||||||
if len(anchorType) == 0 {
|
if len(anchorType) == 0 {
|
||||||
anchorType = AncorTypeApplication
|
anchorType = AncorTypeApplication
|
||||||
}
|
}
|
||||||
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), nil)
|
req, err := c.NewRequest(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()
|
||||||
|
@ -70,8 +68,8 @@ func (c *Client) ListWebhooks(ctx context.Context, anchorType string) (*ListWebh
|
||||||
|
|
||||||
// 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(ctx context.Context, webhookID string) error {
|
func (c *Client) DeleteWebhook(webhookID string) error {
|
||||||
req, err := c.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), nil)
|
req, err := c.NewRequest(http.MethodDelete, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -82,7 +80,7 @@ func (c *Client) DeleteWebhook(ctx context.Context, 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(ctx context.Context, httpReq *http.Request, webhookID string) (*VerifyWebhookResponse, error) {
|
func (c *Client) VerifyWebhookSignature(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"`
|
||||||
|
@ -90,18 +88,16 @@ func (c *Client) VerifyWebhookSignature(ctx context.Context, httpReq *http.Reque
|
||||||
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"`
|
||||||
Event json.RawMessage `json:"webhook_event,omitempty"`
|
WebhookEvent json.RawMessage `json:"webhook_event"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the content
|
// Read the content
|
||||||
var bodyBytes []byte
|
var bodyBytes []byte
|
||||||
if httpReq.Body != nil {
|
if httpReq.Body != nil {
|
||||||
bodyBytes, _ = io.ReadAll(httpReq.Body)
|
bodyBytes, _ = ioutil.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 = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
httpReq.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||||
|
|
||||||
verifyRequest := verifyWebhookSignatureRequest{
|
verifyRequest := verifyWebhookSignatureRequest{
|
||||||
AuthAlgo: httpReq.Header.Get("PAYPAL-AUTH-ALGO"),
|
AuthAlgo: httpReq.Header.Get("PAYPAL-AUTH-ALGO"),
|
||||||
|
@ -110,12 +106,12 @@ func (c *Client) VerifyWebhookSignature(ctx context.Context, httpReq *http.Reque
|
||||||
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,
|
||||||
Event: json.RawMessage(bodyBytes),
|
WebhookEvent: json.RawMessage(bodyBytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
response := &VerifyWebhookResponse{}
|
response := &VerifyWebhookResponse{}
|
||||||
|
|
||||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/verify-webhook-signature"), verifyRequest)
|
req, err := c.NewRequest("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
|
||||||
}
|
}
|
||||||
|
@ -126,19 +122,3 @@ func (c *Client) VerifyWebhookSignature(ctx context.Context, httpReq *http.Reque
|
||||||
|
|
||||||
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,7 +1,6 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -11,9 +10,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(ctx context.Context, wp WebProfile) (*WebProfile, error) {
|
func (c *Client) CreateWebProfile(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(ctx, "POST", url, wp)
|
req, err := c.NewRequest("POST", url, wp)
|
||||||
response := &WebProfile{}
|
response := &WebProfile{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -30,11 +29,11 @@ func (c *Client) CreateWebProfile(ctx context.Context, wp WebProfile) (*WebProfi
|
||||||
// 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(ctx context.Context, profileID string) (*WebProfile, error) {
|
func (c *Client) GetWebProfile(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.NewRequestWithContext(ctx, "GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &wp, err
|
return &wp, err
|
||||||
|
@ -51,14 +50,14 @@ func (c *Client) GetWebProfile(ctx context.Context, profileID string) (*WebProfi
|
||||||
return &wp, nil
|
return &wp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebProfiles retrieves web experience profiles from Paypal
|
// GetWebProfiles retreieves web experience profiles from Paypal
|
||||||
//
|
//
|
||||||
// Endpoint: GET /v1/payment-experience/web-profiles
|
// Endpoint: GET /v1/payment-experience/web-profiles
|
||||||
func (c *Client) GetWebProfiles(ctx context.Context) ([]WebProfile, error) {
|
func (c *Client) GetWebProfiles() ([]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.NewRequestWithContext(ctx, "GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wps, err
|
return wps, err
|
||||||
|
@ -74,7 +73,7 @@ func (c *Client) GetWebProfiles(ctx context.Context) ([]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(ctx context.Context, wp WebProfile) error {
|
func (c *Client) SetWebProfile(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")
|
||||||
|
@ -82,7 +81,7 @@ func (c *Client) SetWebProfile(ctx context.Context, 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(ctx, "PUT", url, wp)
|
req, err := c.NewRequest("PUT", url, wp)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -98,11 +97,11 @@ func (c *Client) SetWebProfile(ctx context.Context, 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(ctx context.Context, profileID string) error {
|
func (c *Client) DeleteWebProfile(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(ctx, "DELETE", url, nil)
|
req, err := c.NewRequest("DELETE", url, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in New Issue
Block a user