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 ./...
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,2 @@
|
|||
.idea
|
||||
.vscode
|
||||
vendor/
|
||||
.vscode
|
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)
|
||||
|
||||
Copyright (c) 2024 Aliaksandr Pliutau
|
||||
Copyright (c) 2019 Aliaksandr Pliutau
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
144
README.md
144
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>
|
||||
<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
|
||||
|
||||
# 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`
|
||||
|
||||
## Usage
|
||||
### New Client
|
||||
|
||||
```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
|
||||
c, err := paypal.NewClient("clientID", "secretID", paypal.APIBaseSandBox)
|
||||
c.SetLog(os.Stdout) // Set log to terminal stdout
|
||||
|
||||
accessToken, err := c.GetAccessToken()
|
||||
```
|
||||
|
||||
## Get authorization by ID
|
||||
### Get authorization by ID
|
||||
|
||||
```go
|
||||
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"})
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
```go
|
||||
|
@ -64,11 +122,7 @@ order, err := c.GetOrder("O-4J082351X3132253H")
|
|||
### Create an Order
|
||||
|
||||
```go
|
||||
ctx := context.Background()
|
||||
units := []paypal.PurchaseUnitRequest{}
|
||||
source := &paypal.PaymentSource{}
|
||||
appCtx := &paypal.ApplicationContext{}
|
||||
order, err := c.CreateOrder(ctx, paypal.OrderIntentCapture, units, ource, appCtx)
|
||||
order, err := c.CreateOrder(paypal.OrderIntentCapture, []paypal.PurchaseUnitRequest{paypal.PurchaseUnitRequest{ReferenceID: "ref-id", Amount: paypal.Amount{Total: "7.00", Currency: "USD"}}})
|
||||
```
|
||||
|
||||
### 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
|
||||
|
@ -186,6 +240,7 @@ webprofiles, err := c.GetWebProfiles()
|
|||
### Update web experience profile
|
||||
|
||||
```go
|
||||
|
||||
webprofile := WebProfile{
|
||||
ID: "XP-CP6S-W9DY-96H8-MVN2",
|
||||
Name: "Shop YeowZa! YeowZa! ",
|
||||
|
@ -233,7 +288,6 @@ c.GetCreditCards(nil)
|
|||
```
|
||||
|
||||
### Webhooks
|
||||
|
||||
```go
|
||||
// Create a webhook
|
||||
c.CreateWebhook(paypal.CreateWebhookRequest{
|
||||
|
@ -241,7 +295,7 @@ c.CreateWebhook(paypal.CreateWebhookRequest{
|
|||
EventTypes: []paypal.WebhookEventType{
|
||||
paypal.WebhookEventType{
|
||||
Name: "PAYMENT.AUTHORIZATION.CREATED",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -266,46 +320,22 @@ c.DeleteWebhook("WebhookID")
|
|||
|
||||
// List registered webhooks
|
||||
c.ListWebhooks(paypal.AncorTypeApplication)
|
||||
|
||||
```
|
||||
|
||||
### Generate Next Invoice Number
|
||||
### How to Contribute
|
||||
|
||||
```go
|
||||
// GenerateInvoiceNumber: generates the next invoice number that is available to the merchant.
|
||||
c.GenerateInvoiceNumber(ctx) // might return something like "0001" or "0010".
|
||||
```
|
||||
* Fork a repository
|
||||
* Add/Fix something
|
||||
* 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)
|
||||
- [Alex Pliutau](https://github.com/plutov)
|
||||
|
||||
## Tests
|
||||
### Tests
|
||||
|
||||
```
|
||||
go test -v ./...
|
||||
```
|
||||
* Unit tests: `go test -v ./...`
|
||||
* Integration tests: `go test -tags=integration`
|
||||
|
|
|
@ -2,16 +2,15 @@ package paypal
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// GetAuthorization returns an authorization by 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(""))
|
||||
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{}
|
||||
|
||||
if err != nil {
|
||||
|
@ -25,19 +24,19 @@ func (c *Client) GetAuthorization(ctx context.Context, authID string) (*Authoriz
|
|||
// CaptureAuthorization captures and process an existing authorization.
|
||||
// To use this method, the original payment must have Intent set to "authorize"
|
||||
// Endpoint: POST /v2/payments/authorizations/ID/capture
|
||||
func (c *Client) CaptureAuthorization(ctx context.Context, authID string, paymentCaptureRequest *PaymentCaptureRequest) (*PaymentCaptureResponse, error) {
|
||||
return c.CaptureAuthorizationWithPaypalRequestId(ctx, authID, paymentCaptureRequest, "")
|
||||
func (c *Client) CaptureAuthorization(authID string, paymentCaptureRequest *PaymentCaptureRequest) (*PaymentCaptureResponse, error) {
|
||||
return c.CaptureAuthorizationWithPaypalRequestId(authID, paymentCaptureRequest, "")
|
||||
}
|
||||
|
||||
// CaptureAuthorization captures and process an existing authorization with idempotency.
|
||||
// To use this method, the original payment must have Intent set to "authorize"
|
||||
// Endpoint: POST /v2/payments/authorizations/ID/capture
|
||||
func (c *Client) CaptureAuthorizationWithPaypalRequestId(ctx context.Context,
|
||||
func (c *Client) CaptureAuthorizationWithPaypalRequestId(
|
||||
authID string,
|
||||
paymentCaptureRequest *PaymentCaptureRequest,
|
||||
requestID string,
|
||||
) (*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{}
|
||||
|
||||
if err != nil {
|
||||
|
@ -54,9 +53,9 @@ func (c *Client) CaptureAuthorizationWithPaypalRequestId(ctx context.Context,
|
|||
|
||||
// VoidAuthorization voids a previously authorized payment
|
||||
// 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(""))
|
||||
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{}
|
||||
|
||||
if err != nil {
|
||||
|
@ -68,11 +67,11 @@ func (c *Client) VoidAuthorization(ctx context.Context, authID string) (*Authori
|
|||
}
|
||||
|
||||
// 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
|
||||
func (c *Client) ReauthorizeAuthorization(ctx context.Context, authID string, a *Amount) (*Authorization, error) {
|
||||
buf := bytes.NewBuffer([]byte(`{"amount":{"currency_code":"` + a.Currency + `","value":"` + a.Total + `"}}`))
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/reauthorize"), buf)
|
||||
func (c *Client) ReauthorizeAuthorization(authID string, a *Amount) (*Authorization, error) {
|
||||
buf := bytes.NewBuffer([]byte(`{"amount":{"currency":"` + a.Currency + `","total":"` + a.Total + `"}}`))
|
||||
req, err := http.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v2/payments/authorizations/"+authID+"/reauthorize"), buf)
|
||||
auth := &Authorization{}
|
||||
|
||||
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
|
||||
}
|
82
client.go
82
client.go
|
@ -2,11 +2,11 @@ package paypal
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"time"
|
||||
|
@ -30,9 +30,9 @@ func NewClient(clientID string, secret string, APIBase string) (*Client, error)
|
|||
// GetAccessToken returns struct of TokenResponse
|
||||
// No need to call SetAccessToken to apply new access token for current Client
|
||||
// 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"))
|
||||
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 {
|
||||
return &TokenResponse{}, err
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ func (c *Client) SetReturnRepresentation() {
|
|||
}
|
||||
|
||||
// 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
|
||||
func (c *Client) Send(req *http.Request, v interface{}) error {
|
||||
var (
|
||||
|
@ -97,36 +97,21 @@ func (c *Client) Send(req *http.Request, v interface{}) error {
|
|||
if c.returnRepresentation {
|
||||
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)
|
||||
c.log(req, resp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||
errResp := &ErrorResponse{Response: resp}
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
|
||||
if err == nil && len(data) > 0 {
|
||||
err := json.Unmarshal(data, errResp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json.Unmarshal(data, errResp)
|
||||
}
|
||||
|
||||
return errResp
|
||||
|
@ -136,8 +121,8 @@ func (c *Client) Send(req *http.Request, v interface{}) error {
|
|||
}
|
||||
|
||||
if w, ok := v.(io.Writer); ok {
|
||||
_, err := io.Copy(w, resp.Body)
|
||||
return err
|
||||
io.Copy(w, resp.Body)
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
// client.Token will be updated when changed
|
||||
func (c *Client) SendWithAuth(req *http.Request, v interface{}) error {
|
||||
// c.Lock()
|
||||
c.mu.Lock()
|
||||
c.Lock()
|
||||
// Note: Here we do not want to `defer c.Unlock()` because we need `c.Send(...)`
|
||||
// to happen outside of the locked section.
|
||||
|
||||
if c.Token == nil || (!c.tokenExpiresAt.IsZero() && time.Until(c.tokenExpiresAt) < RequestNewTokenBeforeExpiresIn) {
|
||||
// c.Token will be updated in GetAccessToken call
|
||||
if _, err := c.GetAccessToken(req.Context()); err != nil {
|
||||
// c.Unlock()
|
||||
c.mu.Unlock()
|
||||
return err
|
||||
if c.Token != nil {
|
||||
if !c.tokenExpiresAt.IsZero() && c.tokenExpiresAt.Sub(time.Now()) < RequestNewTokenBeforeExpiresIn {
|
||||
// c.Token will be updated in GetAccessToken call
|
||||
if _, err := c.GetAccessToken(); err != nil {
|
||||
c.Unlock()
|
||||
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
|
||||
// to be in progress at the same time.
|
||||
// c.Unlock()
|
||||
c.mu.Unlock()
|
||||
c.Unlock()
|
||||
return c.Send(req, v)
|
||||
}
|
||||
|
||||
|
@ -179,7 +164,7 @@ func (c *Client) SendWithBasicAuth(req *http.Request, v interface{}) error {
|
|||
|
||||
// NewRequest constructs a request
|
||||
// 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
|
||||
if payload != nil {
|
||||
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)
|
||||
}
|
||||
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
|
||||
|
||||
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
|
||||
)
|
||||
go 1.12
|
||||
|
|
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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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.
|
||||
// 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{}
|
||||
|
||||
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 {
|
||||
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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
// GetOrder retrieves order by 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{}
|
||||
|
||||
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 {
|
||||
return order, err
|
||||
}
|
||||
|
@ -23,39 +19,23 @@ func (c *Client) GetOrder(ctx context.Context, orderID string) (*Order, error) {
|
|||
return order, nil
|
||||
}
|
||||
|
||||
// Create an order
|
||||
// CreateOrder - Use this call to create an order
|
||||
// Endpoint: POST /v2/checkout/orders
|
||||
func (c *Client) CreateOrder(ctx context.Context, intent string, purchaseUnits []PurchaseUnitRequest, paymentSource *PaymentSource, appContext *ApplicationContext) (*Order, error) {
|
||||
return c.CreateOrderWithPaypalRequestID(ctx, intent, purchaseUnits, paymentSource, appContext, "")
|
||||
}
|
||||
|
||||
// CreateOrderWithPaypalRequestID - Use this call to create an order with idempotency
|
||||
// Endpoint: POST /v2/checkout/orders
|
||||
func (c *Client) CreateOrderWithPaypalRequestID(ctx context.Context,
|
||||
intent string,
|
||||
purchaseUnits []PurchaseUnitRequest,
|
||||
paymentSource *PaymentSource,
|
||||
appContext *ApplicationContext,
|
||||
requestID string,
|
||||
) (*Order, error) {
|
||||
func (c *Client) CreateOrder(intent string, purchaseUnits []PurchaseUnitRequest, payer *CreateOrderPayer, appContext *ApplicationContext) (*Order, error) {
|
||||
type createOrderRequest struct {
|
||||
Intent string `json:"intent"`
|
||||
PaymentSource *PaymentSource `json:"payment_source,omitempty"`
|
||||
Payer *CreateOrderPayer `json:"payer,omitempty"`
|
||||
PurchaseUnits []PurchaseUnitRequest `json:"purchase_units"`
|
||||
ApplicationContext *ApplicationContext `json:"application_context,omitempty"`
|
||||
}
|
||||
|
||||
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 {
|
||||
return order, err
|
||||
}
|
||||
|
||||
if requestID != "" {
|
||||
req.Header.Set("PayPal-Request-Id", requestID)
|
||||
}
|
||||
|
||||
if err = c.SendWithAuth(req, order); err != nil {
|
||||
return order, err
|
||||
}
|
||||
|
@ -65,37 +45,27 @@ func (c *Client) CreateOrderWithPaypalRequestID(ctx context.Context,
|
|||
|
||||
// UpdateOrder updates the order by 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 {
|
||||
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,
|
||||
},
|
||||
})
|
||||
req, err := c.NewRequest("PATCH", fmt.Sprintf("%s%s%s", c.APIBase, "/v2/checkout/orders/", orderID), purchaseUnits)
|
||||
if err != nil {
|
||||
return err
|
||||
return order, err
|
||||
}
|
||||
|
||||
if err = c.SendWithAuth(req, nil); err != nil {
|
||||
return err
|
||||
if err = c.SendWithAuth(req, order); err != nil {
|
||||
return order, err
|
||||
}
|
||||
return nil
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
// AuthorizeOrder - https://developer.paypal.com/docs/api/orders/v2/#orders_authorize
|
||||
// Endpoint: POST /v2/checkout/orders/ID/authorize
|
||||
func (c *Client) AuthorizeOrder(ctx context.Context, orderID string, authorizeOrderRequest AuthorizeOrderRequest) (*AuthorizeOrderResponse, error) {
|
||||
auth := &AuthorizeOrderResponse{}
|
||||
func (c *Client) AuthorizeOrder(orderID string, authorizeOrderRequest AuthorizeOrderRequest) (*Authorization, error) {
|
||||
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 {
|
||||
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
|
||||
// Endpoint: POST /v2/checkout/orders/ID/capture
|
||||
func (c *Client) CaptureOrder(ctx context.Context, orderID string, captureOrderRequest CaptureOrderRequest) (*CaptureOrderResponse, error) {
|
||||
return c.CaptureOrderWithPaypalRequestId(ctx, orderID, captureOrderRequest, "", nil)
|
||||
func (c *Client) CaptureOrder(orderID string, captureOrderRequest CaptureOrderRequest) (*CaptureOrderResponse, error) {
|
||||
return c.CaptureOrderWithPaypalRequestId(orderID, captureOrderRequest, "")
|
||||
}
|
||||
|
||||
// CaptureOrder with idempotency - https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
||||
// Endpoint: POST /v2/checkout/orders/ID/capture
|
||||
// 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,
|
||||
captureOrderRequest CaptureOrderRequest,
|
||||
requestID string,
|
||||
mockResponse *CaptureOrderMockResponse,
|
||||
) (*CaptureOrderResponse, error) {
|
||||
capture := &CaptureOrderResponse{}
|
||||
|
||||
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 {
|
||||
return capture, err
|
||||
}
|
||||
|
@ -134,15 +103,6 @@ func (c *Client) CaptureOrderWithPaypalRequestId(ctx context.Context,
|
|||
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 {
|
||||
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
|
||||
// Endpoint: POST /v2/payments/captures/ID/refund
|
||||
func (c *Client) RefundCapture(ctx context.Context, captureID string, refundCaptureRequest RefundCaptureRequest) (*RefundResponse, error) {
|
||||
return c.RefundCaptureWithPaypalRequestId(ctx, captureID, refundCaptureRequest, "")
|
||||
func (c *Client) RefundCapture(captureID string, refundCaptureRequest RefundCaptureRequest) (*RefundResponse, error) {
|
||||
return c.RefundCaptureWithPaypalRequestId(captureID, refundCaptureRequest, "")
|
||||
}
|
||||
|
||||
// RefundCapture with idempotency - https://developer.paypal.com/docs/api/payments/v2/#captures_refund
|
||||
// Endpoint: POST /v2/payments/captures/ID/refund
|
||||
func (c *Client) RefundCaptureWithPaypalRequestId(ctx context.Context,
|
||||
func (c *Client) RefundCaptureWithPaypalRequestId(
|
||||
captureID string,
|
||||
refundCaptureRequest RefundCaptureRequest,
|
||||
requestID string,
|
||||
) (*RefundResponse, error) {
|
||||
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 {
|
||||
return refund, err
|
||||
}
|
||||
|
@ -179,19 +139,3 @@ func (c *Client) RefundCaptureWithPaypalRequestId(ctx context.Context,
|
|||
}
|
||||
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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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
|
||||
// Endpoint: POST /v1/payments/payouts
|
||||
func (c *Client) CreatePayout(ctx context.Context, p Payout) (*PayoutResponse, error) {
|
||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts"), p)
|
||||
func (c *Client) CreateSinglePayout(p Payout) (*PayoutResponse, error) {
|
||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts"), p)
|
||||
response := &PayoutResponse{}
|
||||
|
||||
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.
|
||||
// Also, returns IDs for the individual payout items. You can use these item IDs in other calls.
|
||||
// Endpoint: GET /v1/payments/payouts/ID
|
||||
func (c *Client) GetPayout(ctx context.Context, payoutBatchID string) (*PayoutResponse, error) {
|
||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts/"+payoutBatchID), nil)
|
||||
func (c *Client) GetPayout(payoutBatchID string) (*PayoutResponse, error) {
|
||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts/"+payoutBatchID), nil)
|
||||
response := &PayoutResponse{}
|
||||
|
||||
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.
|
||||
// Use this call to review the current status of a previously unclaimed, or pending, payout item.
|
||||
// Endpoint: GET /v1/payments/payouts-item/ID
|
||||
func (c *Client) GetPayoutItem(ctx context.Context, payoutItemID string) (*PayoutItemResponse, error) {
|
||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID), nil)
|
||||
func (c *Client) GetPayoutItem(payoutItemID string) (*PayoutItemResponse, error) {
|
||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID), nil)
|
||||
response := &PayoutItemResponse{}
|
||||
|
||||
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,
|
||||
// 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
|
||||
func (c *Client) CancelPayoutItem(ctx context.Context, payoutItemID string) (*PayoutItemResponse, error) {
|
||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID+"/cancel"), nil)
|
||||
func (c *Client) CancelPayoutItem(payoutItemID string) (*PayoutItemResponse, error) {
|
||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/payouts-item/"+payoutItemID+"/cancel"), nil)
|
||||
response := &PayoutItemResponse{}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
import "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{}
|
||||
|
||||
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 {
|
||||
return refund, err
|
||||
}
|
||||
|
|
225
subscription.go
225
subscription.go
|
@ -1,149 +1,33 @@
|
|||
package paypal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
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 {
|
||||
SubscriptionBase
|
||||
SubscriptionDetails
|
||||
BillingInfo BillingInfo `json:"billing_info,omitempty"` // not found in documentation
|
||||
SharedResponse
|
||||
}
|
||||
|
||||
SubscriptionCaptureResponse struct {
|
||||
Status SubscriptionTransactionStatus `json:"status"`
|
||||
Id string `json:"id"`
|
||||
AmountWithBreakdown AmountWithBreakdown `json:"amount_with_breakdown"`
|
||||
PayerName Name `json:"payer_name"`
|
||||
PayerEmail string `json:"payer_email"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
//Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-amount_with_breakdown
|
||||
AmountWithBreakdown struct {
|
||||
GrossAmount Money `json:"gross_amount"`
|
||||
FeeAmount Money `json:"fee_amount"`
|
||||
ShippingAmount Money `json:"shipping_amount"`
|
||||
TaxAmount Money `json:"tax_amount"`
|
||||
NetAmount Money `json:"net_amount"`
|
||||
}
|
||||
|
||||
SubscriptionTransactionsParams struct {
|
||||
SubscriptionId string
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
SubscriptionTransactionsResponse struct {
|
||||
Transactions []SubscriptionCaptureResponse `json:"transactions"`
|
||||
SharedListResponse
|
||||
}
|
||||
|
||||
CaptureRequest struct {
|
||||
Note string `json:"note"`
|
||||
CaptureType CaptureType `json:"capture_type"`
|
||||
Amount Money `json:"amount"`
|
||||
}
|
||||
|
||||
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-plan_override
|
||||
PlanOverride struct {
|
||||
BillingCycles []BillingCycleOverride `json:"billing_cycles,omitempty"`
|
||||
PaymentPreferences *PaymentPreferencesOverride `json:"payment_preferences,omitempty"`
|
||||
Taxes *TaxesOverride `json:"taxes,omitempty"`
|
||||
}
|
||||
|
||||
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-payment_preferences_override
|
||||
PaymentPreferencesOverride struct {
|
||||
AutoBillOutstanding bool `json:"auto_bill_outstanding,omitempty"`
|
||||
SetupFee Money `json:"setup_fee,omitempty"`
|
||||
SetupFeeFailureAction SetupFeeFailureAction `json:"setup_fee_failure_action,omitempty"`
|
||||
PaymentFailureThreshold int `json:"payment_failure_threshold,omitempty"`
|
||||
}
|
||||
|
||||
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-payment_preferences_override
|
||||
TaxesOverride struct {
|
||||
Percentage string `json:"percentage,omitempty"`
|
||||
Inclusive *bool `json:"inclusive,omitempty"`
|
||||
}
|
||||
|
||||
// https://developer.paypal.com/docs/api/subscriptions/v1/#definition-billing_cycle_override
|
||||
BillingCycleOverride struct {
|
||||
PricingScheme PricingScheme `json:"pricing_scheme,omitempty"`
|
||||
Sequence *int `json:"sequence,omitempty"`
|
||||
TotalCycles *int `json:"total_cycles,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
PlanID string `json:"plan_id,omitempty"`
|
||||
StartTime time.Time `json:"start_time,omitempty"`
|
||||
Quantity string `json:"quantity,omitempty"`
|
||||
ShippingAmount ShippingAmount `json:"shipping_amount,omitempty"`
|
||||
Subscriber Subscriber `json:"subscriber,omitempty"`
|
||||
BillingInfo BillingInfo `json:"billing_info,omitempty"`
|
||||
CreateTime time.Time `json:"create_time,omitempty"`
|
||||
UpdateTime time.Time `json:"update_time,omitempty"`
|
||||
Links []Link `json:"links,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
StatusUpdateTime time.Time `json:"status_update_time,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.
|
||||
// Endpoint: GET /v1/billing/subscriptions/
|
||||
func (c *Client) GetSubscriptionDetails(ctx context.Context, subscriptionID string) (*SubscriptionDetailResp, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
||||
func (c *Client) GetSubscriptionDetails(subscriptionID string) (*SubscriptionDetailResp, error) {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
||||
response := &SubscriptionDetailResp{}
|
||||
if err != nil {
|
||||
return response, err
|
||||
|
@ -151,84 +35,3 @@ func (c *Client) GetSubscriptionDetails(ctx context.Context, subscriptionID stri
|
|||
err = c.SendWithAuth(req, response)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// Activates the subscription.
|
||||
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_activate
|
||||
// Endpoint: POST /v1/billing/subscriptions/{id}/activate
|
||||
func (c *Client) ActivateSubscription(ctx context.Context, subscriptionId, activateReason string) error {
|
||||
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/activate", c.APIBase, subscriptionId), map[string]string{"reason": activateReason})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.SendWithAuth(req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Cancels the subscription.
|
||||
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_cancel
|
||||
// Endpoint: POST /v1/billing/subscriptions/{id}/cancel
|
||||
func (c *Client) CancelSubscription(ctx context.Context, subscriptionId, cancelReason string) error {
|
||||
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/cancel", c.APIBase, subscriptionId), map[string]string{"reason": cancelReason})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.SendWithAuth(req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Captures an authorized payment from the subscriber on the subscription.
|
||||
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_capture
|
||||
// Endpoint: POST /v1/billing/subscriptions/{id}/capture
|
||||
func (c *Client) CaptureSubscription(ctx context.Context, subscriptionId string, request CaptureRequest) (*SubscriptionCaptureResponse, error) {
|
||||
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/capture", c.APIBase, subscriptionId), request)
|
||||
response := &SubscriptionCaptureResponse{}
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
err = c.SendWithAuth(req, response)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// Suspends the subscription.
|
||||
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_suspend
|
||||
// Endpoint: POST /v1/billing/subscriptions/{id}/suspend
|
||||
func (c *Client) SuspendSubscription(ctx context.Context, subscriptionId, reason string) error {
|
||||
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/suspend", c.APIBase, subscriptionId), map[string]string{"reason": reason})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.SendWithAuth(req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Lists transactions for a subscription.
|
||||
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_transactions
|
||||
// Endpoint: GET /v1/billing/subscriptions/{id}/transactions
|
||||
func (c *Client) GetSubscriptionTransactions(ctx context.Context, requestParams SubscriptionTransactionsParams) (*SubscriptionTransactionsResponse, error) {
|
||||
startTime := requestParams.StartTime.Format("2006-01-02T15:04:05Z")
|
||||
endTime := requestParams.EndTime.Format("2006-01-02T15:04:05Z")
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/v1/billing/subscriptions/%s/transactions?start_time=%s&end_time=%s", c.APIBase, requestParams.SubscriptionId, startTime, endTime), nil)
|
||||
response := &SubscriptionTransactionsResponse{}
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
err = c.SendWithAuth(req, response)
|
||||
return response, err
|
||||
}
|
||||
|
||||
// Revise plan or quantity of subscription
|
||||
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_revise
|
||||
// Endpoint: POST /v1/billing/subscriptions/{id}/revise
|
||||
func (c *Client) ReviseSubscription(ctx context.Context, subscriptionId string, reviseSubscription SubscriptionBase) (*SubscriptionDetailResp, error) {
|
||||
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s/v1/billing/subscriptions/%s/revise", c.APIBase, subscriptionId), reviseSubscription)
|
||||
response := &SubscriptionDetailResp{}
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
err = c.SendWithAuth(req, response)
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -31,15 +30,17 @@ type TransactionSearchResponse struct {
|
|||
EndDate JSONTime `json:"end_date"`
|
||||
LastRefreshDatetime JSONTime `json:"last_refreshed_datetime"`
|
||||
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.
|
||||
// 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{}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
621
types.go
621
types.go
|
@ -12,10 +12,10 @@ import (
|
|||
|
||||
const (
|
||||
// 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 = "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 = time.Duration(60) * time.Second
|
||||
|
@ -85,9 +85,13 @@ const (
|
|||
// Possible values for `shipping_preference` in ApplicationContext
|
||||
//
|
||||
// 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 (
|
||||
EventCheckoutOrderApproved string = "CHECKOUT.ORDER.APPROVED"
|
||||
EventPaymentCaptureCompleted string = "PAYMENT.CAPTURE.COMPLETED"
|
||||
EventPaymentCaptureDenied string = "PAYMENT.CAPTURE.DENIED"
|
||||
EventPaymentCaptureRefunded string = "PAYMENT.CAPTURE.REFUNDED"
|
||||
|
@ -116,31 +120,6 @@ const (
|
|||
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 (
|
||||
LinkRelSelf string = "self"
|
||||
LinkRelActionURL string = "action_url"
|
||||
|
@ -157,10 +136,10 @@ type (
|
|||
|
||||
// Address struct
|
||||
Address struct {
|
||||
Line1 string `json:"line1,omitempty"`
|
||||
Line1 string `json:"line1"`
|
||||
Line2 string `json:"line2,omitempty"`
|
||||
City string `json:"city,omitempty"`
|
||||
CountryCode string `json:"country_code,omitempty"`
|
||||
City string `json:"city"`
|
||||
CountryCode string `json:"country_code"`
|
||||
PostalCode string `json:"postal_code,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
|
@ -192,280 +171,14 @@ type (
|
|||
}
|
||||
|
||||
// ApplicationContext struct
|
||||
//Doc: https://developer.paypal.com/docs/api/orders/v2/#definition-application_context
|
||||
ApplicationContext struct {
|
||||
BrandName string `json:"brand_name,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"`
|
||||
ReturnURL string `json:"return_url,omitempty"`
|
||||
CancelURL string `json:"cancel_url,omitempty"`
|
||||
}
|
||||
|
||||
// Invoicing relates structures
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#invoices_generate-next-invoice-number
|
||||
InvoiceNumber struct {
|
||||
InvoiceNumberValue string `json:"invoice_number"`
|
||||
}
|
||||
|
||||
// used in InvoiceAmountWithBreakdown
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-custom_amount
|
||||
CustomAmount struct {
|
||||
Label string `json:"label"`
|
||||
Amount Money `json:"amount,omitempty"`
|
||||
}
|
||||
// Used in AggregatedDiscount
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-discount
|
||||
InvoicingDiscount struct {
|
||||
DiscountAmount Money `json:"amount,omitempty"`
|
||||
Percent string `json:"percent,omitempty"`
|
||||
}
|
||||
// Used in InvoiceAmountWithBreakdown
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-aggregated_discount
|
||||
AggregatedDiscount struct {
|
||||
InvoiceDiscount InvoicingDiscount `json:"invoice_discount,omitempty"`
|
||||
ItemDiscount *Money `json:"item_discount,omitempty"`
|
||||
}
|
||||
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-tax
|
||||
InvoiceTax struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Percent string `json:"percent,omitempty"`
|
||||
ID string `json:"id,omitempty"` // not mentioned here, but is still returned in response payload, when invoice is requested by ID.
|
||||
Amount Money `json:"amount,omitempty"`
|
||||
}
|
||||
// Used in InvoiceAmountWithBreakdown struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-shipping_cost
|
||||
InvoiceShippingCost struct {
|
||||
Amount Money `json:"amount,omitempty"`
|
||||
Tax InvoiceTax `json:"tax,omitempty"`
|
||||
}
|
||||
|
||||
// Used in AmountSummaryDetail
|
||||
// Doc: https://developer.paypal.com/docs/api/payments/v2/#definition-nrp-nrr_attributes
|
||||
InvoiceAmountWithBreakdown struct {
|
||||
Custom CustomAmount `json:"custom,omitempty"` // The custom amount to apply to an invoice.
|
||||
Discount AggregatedDiscount `json:"discount,omitempty"`
|
||||
ItemTotal Money `json:"item_total,omitempty"` // The subtotal for all items.
|
||||
Shipping InvoiceShippingCost `json:"shipping,omitempty"` // The shipping fee for all items. Includes tax on shipping.
|
||||
TaxTotal Money `json:"tax_total,omitempty"`
|
||||
}
|
||||
|
||||
// Invoice AmountSummary
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-amount_summary_detail
|
||||
AmountSummaryDetail struct {
|
||||
Breakdown InvoiceAmountWithBreakdown `json:"breakdown,omitempty"`
|
||||
Currency string `json:"currency_code,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-partial_payment
|
||||
InvoicePartialPayment struct {
|
||||
AllowPartialPayment bool `json:"allow_partial_payment,omitempty"`
|
||||
MinimumAmountDue Money `json:"minimum_amount_due,omitempty"` // Valid only when allow_partial_payment is true.
|
||||
}
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-configuration
|
||||
InvoiceConfiguration struct {
|
||||
AllowTip bool `json:"allow_tip,omitempty"`
|
||||
PartialPayment InvoicePartialPayment `json:"partial_payment,omitempty"`
|
||||
TaxCalculatedAfterDiscount bool `json:"tax_calculated_after_discount,omitempty"`
|
||||
TaxInclusive bool `json:"tax_inclusive,omitempty"`
|
||||
TemplateId string `json:"template_id,omitempty"`
|
||||
}
|
||||
// used in InvoiceDetail structure
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-file_reference
|
||||
InvoiceFileReference struct {
|
||||
ContentType string `json:"content_type,omitempty"`
|
||||
CreateTime string `json:"create_time,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
URL string `json:"reference_url,omitempty"`
|
||||
Size string `json:"size,omitempty"`
|
||||
}
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-metadata
|
||||
InvoiceAuditMetadata struct {
|
||||
CreateTime string `json:"create_time,omitempty"`
|
||||
CreatedBy string `json:"created_by,omitempty"`
|
||||
LastUpdateTime string `json:"last_update_time,omitempty"`
|
||||
LastUpdatedBy string `json:"last_updated_by,omitempty"`
|
||||
CancelTime string `json:"cancel_time,omitempty"`
|
||||
CancellledTimeBy string `json:"cancelled_by,omitempty"`
|
||||
CreatedByFlow string `json:"created_by_flow,omitempty"`
|
||||
FirstSentTime string `json:"first_sent_time,omitempty"`
|
||||
InvoicerViewUrl string `json:"invoicer_view_url,omitempty"`
|
||||
LastSentBy string `json:"last_sent_by,omitempty"`
|
||||
LastSentTime string `json:"last_sent_time,omitempty"`
|
||||
RecipientViewUrl string `json:"recipient_view_url,omitempty"`
|
||||
}
|
||||
// used in InvoiceDetail struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-invoice_payment_term
|
||||
InvoicePaymentTerm struct {
|
||||
TermType string `json:"term_type,omitempty"`
|
||||
DueDate string `json:"due_date,omitempty"`
|
||||
}
|
||||
|
||||
// used in Invoice struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-invoice_detail
|
||||
InvoiceDetail struct {
|
||||
CurrencyCode string `json:"currency_code"` // required, hence omitempty not used
|
||||
Attachments []InvoiceFileReference `json:"attachments,omitempty"`
|
||||
Memo string `json:"memo,omitempty"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
TermsAndConditions string `json:"terms_and_conditions,omitempty"`
|
||||
InvoiceDate string `json:"invoice_date,omitempty"`
|
||||
InvoiceNumber string `json:"invoice_number,omitempty"`
|
||||
Metadata InvoiceAuditMetadata `json:"metadata,omitempty"` // The audit metadata.
|
||||
PaymentTerm InvoicePaymentTerm `json:"payment_term,omitempty"` // payment due date for the invoice. Value is either but not both term_type or due_date.
|
||||
}
|
||||
|
||||
// used in InvoicerInfo struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-phone_detail
|
||||
InvoicerPhoneDetail struct {
|
||||
CountryCode string `json:"country_code"`
|
||||
NationalNumber string `json:"national_number"`
|
||||
ExtensionNumber string `json:"extension_number,omitempty"`
|
||||
PhoneType string `json:"phone_type,omitempty"`
|
||||
}
|
||||
|
||||
// used in Invoice struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-invoicer_info
|
||||
InvoicerInfo struct {
|
||||
AdditionalNotes string `json:"additional_notes,omitempty"`
|
||||
EmailAddress string `json:"email_address,omitempty"`
|
||||
LogoUrl string `json:"logo_url,omitempty"`
|
||||
Phones []InvoicerPhoneDetail `json:"phones,omitempty"`
|
||||
TaxId string `json:"tax_id,omitempty"`
|
||||
Website string `json:"website,omitempty"`
|
||||
}
|
||||
// Used in Invoice struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-item
|
||||
InvoiceItem struct {
|
||||
Name string `json:"name"`
|
||||
Quantity string `json:"quantity"`
|
||||
UnitAmount Money `json:"unit_amount"`
|
||||
Description string `json:"description,omitempty"`
|
||||
InvoiceDiscount InvoicingDiscount `json:"discount,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
ItemDate string `json:"item_date,omitempty"`
|
||||
Tax InvoiceTax `json:"tax,omitempty"`
|
||||
UnitOfMeasure string `json:"unit_of_measure,omitempty"`
|
||||
}
|
||||
|
||||
// used in InvoiceAddressPortable
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-address_details
|
||||
InvoiceAddressDetails struct {
|
||||
BuildingName string `json:"building_name,omitempty"`
|
||||
DeliveryService string `json:"delivery_service,omitempty"`
|
||||
StreetName string `json:"street_name,omitempty"`
|
||||
StreetNumber string `json:"street_number,omitempty"`
|
||||
StreetType string `json:"street_type,omitempty"`
|
||||
SubBuilding string `json:"sub_building,omitempty"`
|
||||
}
|
||||
|
||||
// used in InvoiceContactInfo
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-address_portable
|
||||
InvoiceAddressPortable struct {
|
||||
CountryCode string `json:"country_code"`
|
||||
AddressDetails InvoiceAddressDetails `json:"address_details,omitempty"`
|
||||
AddressLine1 string `json:"address_line_1,omitempty"`
|
||||
AddressLine2 string `json:"address_line_2,omitempty"`
|
||||
AddressLine3 string `json:"address_line_3,omitempty"`
|
||||
AdminArea1 string `json:"admin_area_1,omitempty"`
|
||||
AdminArea2 string `json:"admin_area_2,omitempty"`
|
||||
AdminArea3 string `json:"admin_area_3,omitempty"`
|
||||
AdminArea4 string `json:"admin_area_4,omitempty"`
|
||||
PostalCode string `json:"postal_code,omitempty"`
|
||||
}
|
||||
|
||||
// used in InvoicePaymentDetails
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-contact_information
|
||||
InvoiceContactInfo struct {
|
||||
BusinessName string `json:"business_name,omitempty"`
|
||||
RecipientAddress InvoiceAddressPortable `json:"address,omitempty"` // address of the recipient.
|
||||
RecipientName Name `json:"name,omitempty"` // The first and Last name of the recipient.
|
||||
}
|
||||
//used in InvoicePayments struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-payment_detail
|
||||
InvoicePaymentDetails struct {
|
||||
Method string `json:"method"`
|
||||
Amount Money `json:"amount,omitempty"`
|
||||
Note string `json:"note,omitempty"`
|
||||
PaymentDate string `json:"payment_date,omitempty"`
|
||||
PaymentID string `json:"payment_id,omitempty"`
|
||||
ShippingInfo InvoiceContactInfo `json:"shipping_info,omitempty"` // The recipient's shipping information.
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// used in Invoice
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-payments
|
||||
InvoicePayments struct {
|
||||
PaidAmount Money `json:"paid_amount,omitempty"`
|
||||
Transactions []InvoicePaymentDetails `json:"transactions,omitempty"`
|
||||
}
|
||||
|
||||
// used in InvoiceRecipientInfo
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-billing_info
|
||||
InvoiceBillingInfo struct {
|
||||
AdditionalInfo string `json:"additional_info,omitempty"`
|
||||
EmailAddress string `json:"email_address,omitempty"`
|
||||
Language string `json:"language,omitempty"`
|
||||
Phones []InvoicerPhoneDetail `json:"phones,omitempty"` // invoice recipient's phone numbers.
|
||||
}
|
||||
// used in Invoice struct
|
||||
// Doc:
|
||||
InvoiceRecipientInfo struct {
|
||||
BillingInfo InvoiceBillingInfo `json:"billing_info,omitempty"` // billing information for the invoice recipient.
|
||||
ShippingInfo InvoiceContactInfo `json:"shipping_info,omitempty"` // recipient's shipping information.
|
||||
}
|
||||
|
||||
// used in InvoiceRefund struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-refund_detail
|
||||
InvoiceRefundDetails struct {
|
||||
Method string `json:"method"`
|
||||
RefundAmount Money `json:"amount,omitempty"`
|
||||
RefundDate string `json:"refund_date,omitempty"`
|
||||
RefundID string `json:"refund_id,omitempty"`
|
||||
RefundType string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// used in Invoice struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-refunds
|
||||
InvoiceRefund struct {
|
||||
RefundAmount Money `json:"refund_amount,omitempty"`
|
||||
RefundDetails []InvoiceRefundDetails `json:"transactions,omitempty"`
|
||||
}
|
||||
|
||||
// used in Invoice struct
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#definition-email_address
|
||||
InvoiceEmailAddress struct {
|
||||
EmailAddress string `json:"email_address,omitempty"`
|
||||
}
|
||||
|
||||
// to contain Invoice related fields
|
||||
// Doc: https://developer.paypal.com/docs/api/invoicing/v2/#invoices_get
|
||||
Invoice struct {
|
||||
AdditionalRecipients []InvoiceEmailAddress `json:"additional_recipients,omitempty"` // An array of one or more CC: emails to which notifications are sent.
|
||||
AmountSummary AmountSummaryDetail `json:"amount,omitempty"`
|
||||
Configuration InvoiceConfiguration `json:"configuration,omitempty"`
|
||||
Detail InvoiceDetail `json:"detail,omitempty"`
|
||||
DueAmount Money `json:"due_amount,omitempty"` // balance amount outstanding after payments.
|
||||
Gratuity Money `json:"gratuity,omitempty"` // amount paid by the payer as gratuity to the invoicer.
|
||||
ID string `json:"id,omitempty"`
|
||||
Invoicer InvoicerInfo `json:"invoicer,omitempty"`
|
||||
Items []InvoiceItem `json:"items,omitempty"`
|
||||
Links []Link `json:"links,omitempty"`
|
||||
ParentID string `json:"parent_id,omitempty"`
|
||||
Payments InvoicePayments `json:"payments,omitempty"`
|
||||
PrimaryRecipients []InvoiceRecipientInfo `json:"primary_recipients,omitempty"`
|
||||
Refunds InvoiceRefund `json:"refunds,omitempty"` // List of refunds against this invoice.
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// Doc: https://developer.paypal.com/api/orders/v2/#definition-payment_method
|
||||
PaymentMethod struct {
|
||||
PayeePreferred PayeePreferred `json:"payee_preferred,omitempty"`
|
||||
StandardEntryClassCode StandardEntryClassCode `json:"standard_entry_class_code,omitempty"`
|
||||
BrandName string `json:"brand_name,omitempty"`
|
||||
Locale string `json:"locale,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"`
|
||||
CancelURL string `json:"cancel_url,omitempty"`
|
||||
}
|
||||
|
||||
// Authorization struct
|
||||
|
@ -483,13 +196,14 @@ type (
|
|||
Links []Link `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
// AuthorizeOrderResponse .
|
||||
AuthorizeOrderResponse struct {
|
||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Intent string `json:"intent,omitempty"`
|
||||
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
||||
PurchaseUnits []PurchaseUnitRequest `json:"purchase_units,omitempty"`
|
||||
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -541,33 +255,11 @@ type (
|
|||
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 struct {
|
||||
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
|
||||
RefundCaptureRequest struct {
|
||||
Amount *Money `json:"amount,omitempty"`
|
||||
|
@ -586,14 +278,15 @@ type (
|
|||
SenderBatchHeader *SenderBatchHeader `json:"sender_batch_header,omitempty"`
|
||||
}
|
||||
|
||||
// Plan struct
|
||||
Plan struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
CreateTime string `json:"create_time,omitempty"`
|
||||
UpdateTime string `json:"update_time,omitempty"`
|
||||
PaymentDefinitions []PaymentDefinition `json:"payment_definitions,omitempty"`
|
||||
// BillingAgreement struct
|
||||
BillingAgreement struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
StartDate JSONTime `json:"start_date,omitempty"`
|
||||
Plan BillingPlan `json:"plan,omitempty"`
|
||||
Payer Payer `json:"payer,omitempty"`
|
||||
ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"`
|
||||
OverrideMerchantPreferences *MerchantPreferences `json:"override_merchant_preferences,omitempty"`
|
||||
}
|
||||
|
||||
// BillingInfo struct
|
||||
|
@ -617,14 +310,13 @@ type (
|
|||
|
||||
// Capture struct
|
||||
Capture struct {
|
||||
ID string `json:"id,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"`
|
||||
CreateTime *time.Time `json:"create_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"`
|
||||
}
|
||||
|
||||
|
@ -636,8 +328,7 @@ type (
|
|||
|
||||
// Client represents a Paypal REST API Client
|
||||
Client struct {
|
||||
// sync.Mutex
|
||||
mu sync.Mutex
|
||||
sync.Mutex
|
||||
Client *http.Client
|
||||
ClientID string
|
||||
Secret string
|
||||
|
@ -667,8 +358,10 @@ type (
|
|||
|
||||
// CreditCards GET /v1/vault/credit-cards
|
||||
CreditCards struct {
|
||||
Items []CreditCard `json:"items"`
|
||||
SharedListResponse
|
||||
Items []CreditCard `json:"items"`
|
||||
Links []Link `json:"links"`
|
||||
TotalItems int `json:"total_items"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
// CreditCardToken struct
|
||||
|
@ -727,12 +420,9 @@ type (
|
|||
|
||||
// ErrorResponseDetail struct
|
||||
ErrorResponseDetail struct {
|
||||
Field string `json:"field"`
|
||||
Issue string `json:"issue"`
|
||||
Name string `json:"name"`
|
||||
Message string `json:"message"`
|
||||
Description string `json:"description"`
|
||||
Links []Link `json:"link"`
|
||||
Field string `json:"field"`
|
||||
Issue string `json:"issue"`
|
||||
Links []Link `json:"link"`
|
||||
}
|
||||
|
||||
// ErrorResponse https://developer.paypal.com/docs/api/errors/
|
||||
|
@ -825,19 +515,10 @@ type (
|
|||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// PurchaseUnit struct
|
||||
PurchaseUnit struct {
|
||||
ReferenceID string `json:"reference_id"`
|
||||
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"`
|
||||
ReferenceID string `json:"reference_id"`
|
||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
||||
}
|
||||
|
||||
// TaxInfo used for orders.
|
||||
|
@ -876,16 +557,15 @@ type (
|
|||
|
||||
// PurchaseUnitRequest struct
|
||||
PurchaseUnitRequest struct {
|
||||
ReferenceID string `json:"reference_id,omitempty"`
|
||||
Amount *PurchaseUnitAmount `json:"amount"`
|
||||
Payee *PayeeForOrders `json:"payee,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CustomID string `json:"custom_id,omitempty"`
|
||||
InvoiceID string `json:"invoice_id,omitempty"`
|
||||
SoftDescriptor string `json:"soft_descriptor,omitempty"`
|
||||
Items []Item `json:"items,omitempty"`
|
||||
Shipping *ShippingDetail `json:"shipping,omitempty"`
|
||||
PaymentInstruction *PaymentInstruction `json:"payment_instruction,omitempty"`
|
||||
ReferenceID string `json:"reference_id,omitempty"`
|
||||
Amount *PurchaseUnitAmount `json:"amount"`
|
||||
Payee *PayeeForOrders `json:"payee,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CustomID string `json:"custom_id,omitempty"`
|
||||
InvoiceID string `json:"invoice_id,omitempty"`
|
||||
SoftDescriptor string `json:"soft_descriptor,omitempty"`
|
||||
Items []Item `json:"items,omitempty"`
|
||||
Shipping *ShippingDetail `json:"shipping,omitempty"`
|
||||
}
|
||||
|
||||
// MerchantPreferences struct
|
||||
|
@ -900,50 +580,25 @@ type (
|
|||
|
||||
// Order struct
|
||||
Order struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Intent string `json:"intent,omitempty"`
|
||||
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
||||
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
||||
Links []Link `json:"links,omitempty"`
|
||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||
}
|
||||
|
||||
// ExchangeRate struct
|
||||
//
|
||||
// https://developer.paypal.com/docs/api/orders/v2/#definition-exchange_rate
|
||||
ExchangeRate struct {
|
||||
SourceCurrency string `json:"source_currency"`
|
||||
TargetCurrency string `json:"target_currency"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// SellerReceivableBreakdown has the detailed breakdown of the capture activity.
|
||||
SellerReceivableBreakdown struct {
|
||||
GrossAmount *Money `json:"gross_amount,omitempty"`
|
||||
PaypalFee *Money `json:"paypal_fee,omitempty"`
|
||||
PaypalFeeInReceivableCurrency *Money `json:"paypal_fee_in_receivable_currency,omitempty"`
|
||||
NetAmount *Money `json:"net_amount,omitempty"`
|
||||
ReceivableAmount *Money `json:"receivable_amount,omitempty"`
|
||||
ExchangeRate *ExchangeRate `json:"exchange_rate,omitempty"`
|
||||
PlatformFees []PlatformFee `json:"platform_fees,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Intent string `json:"intent,omitempty"`
|
||||
PurchaseUnits []PurchaseUnit `json:"purchase_units,omitempty"`
|
||||
Links []Link `json:"links,omitempty"`
|
||||
CreateTime *time.Time `json:"create_time,omitempty"`
|
||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||
}
|
||||
|
||||
// CaptureAmount struct
|
||||
CaptureAmount struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
CustomID string `json:"custom_id,omitempty"`
|
||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
||||
SellerProtection *SellerProtection `json:"seller_protection,omitempty"`
|
||||
SellerReceivableBreakdown *SellerReceivableBreakdown `json:"seller_receivable_breakdown,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
CustomID string `json:"custom_id,omitempty"`
|
||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
||||
}
|
||||
|
||||
// CapturedPayments has the amounts for a captured order
|
||||
CapturedPayments struct {
|
||||
Autthorizations []Authorization `json:"authorizations,omitempty"`
|
||||
Captures []CaptureAmount `json:"captures,omitempty"`
|
||||
Captures []CaptureAmount `json:"captures,omitempty"`
|
||||
}
|
||||
|
||||
// CapturedPurchaseItem are items for a captured order
|
||||
|
@ -968,13 +623,10 @@ type (
|
|||
|
||||
// PayerWithNameAndPhone struct
|
||||
PayerWithNameAndPhone struct {
|
||||
Name *CreateOrderPayerName `json:"name,omitempty"`
|
||||
EmailAddress string `json:"email_address,omitempty"`
|
||||
Phone *PhoneWithType `json:"phone,omitempty"`
|
||||
PayerID string `json:"payer_id,omitempty"`
|
||||
BirthDate string `json:"birth_date,omitempty"`
|
||||
TaxInfo *TaxInfo `json:"tax_info,omitempty"`
|
||||
Address *ShippingDetailAddressPortable `json:"address,omitempty"`
|
||||
Name *CreateOrderPayerName `json:"name,omitempty"`
|
||||
EmailAddress string `json:"email_address,omitempty"`
|
||||
Phone *PhoneWithType `json:"phone,omitempty"`
|
||||
PayerID string `json:"payer_id,omitempty"`
|
||||
}
|
||||
|
||||
// CaptureOrderResponse is the response for capture order
|
||||
|
@ -982,7 +634,6 @@ type (
|
|||
ID string `json:"id,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Payer *PayerWithNameAndPhone `json:"payer,omitempty"`
|
||||
Address *Address `json:"address,omitempty"`
|
||||
PurchaseUnits []CapturedPurchaseUnit `json:"purchase_units,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -1050,44 +701,27 @@ type (
|
|||
|
||||
// PaymentSource structure
|
||||
PaymentSource struct {
|
||||
Card *PaymentSourceCard `json:"card,omitempty"`
|
||||
Token *PaymentSourceToken `json:"token,omitempty"`
|
||||
Paypal *PaymentSourcePaypal `json:"paypal,omitempty"`
|
||||
Card *PaymentSourceCard `json:"card"`
|
||||
Token *PaymentSourceToken `json:"token"`
|
||||
}
|
||||
|
||||
// PaymentSourceCard structure
|
||||
PaymentSourceCard struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Number string `json:"number,omitempty"`
|
||||
Expiry string `json:"expiry,omitempty"`
|
||||
SecurityCode string `json:"security_code,omitempty"`
|
||||
LastDigits string `json:"last_digits,omitempty"`
|
||||
CardType string `json:"card_type,omitempty"`
|
||||
BillingAddress *CardBillingAddress `json:"billing_address,omitempty"`
|
||||
}
|
||||
|
||||
// PaymentSourcePaypal structure
|
||||
PaymentSourcePaypal struct {
|
||||
ExperienceContext PaymentSourcePaypalExperienceContext `json:"experience_context"`
|
||||
}
|
||||
|
||||
PaymentSourcePaypalExperienceContext struct {
|
||||
PaymentMethodPreference string `json:"payment_method_preference"`
|
||||
BrandName string `json:"brand_name"`
|
||||
Locale string `json:"locale"`
|
||||
LandingPage string `json:"landing_page"`
|
||||
ShippingPreference string `json:"shipping_preference"`
|
||||
UserAction string `json:"user_action"`
|
||||
ReturnURL string `json:"return_url"`
|
||||
CancelURL string `json:"cancel_url"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Number string `json:"number"`
|
||||
Expiry string `json:"expiry"`
|
||||
SecurityCode string `json:"security_code"`
|
||||
LastDigits string `json:"last_digits"`
|
||||
CardType string `json:"card_type"`
|
||||
BillingAddress *CardBillingAddress `json:"billing_address"`
|
||||
}
|
||||
|
||||
// CardBillingAddress structure
|
||||
CardBillingAddress struct {
|
||||
AddressLine1 string `json:"address_line_1"`
|
||||
AddressLine2 string `json:"address_line_2,omitempty"`
|
||||
AdminArea2 string `json:"admin_area_2,omitempty"`
|
||||
AddressLine2 string `json:"address_line_2"`
|
||||
AdminArea2 string `json:"admin_area_2"`
|
||||
AdminArea1 string `json:"admin_area_1"`
|
||||
PostalCode string `json:"postal_code"`
|
||||
CountryCode string `json:"country_code"`
|
||||
|
@ -1107,12 +741,11 @@ type (
|
|||
|
||||
// PayoutItem struct
|
||||
PayoutItem struct {
|
||||
RecipientType string `json:"recipient_type"`
|
||||
RecipientWallet string `json:"recipient_wallet"`
|
||||
Receiver string `json:"receiver"`
|
||||
Amount *AmountPayout `json:"amount"`
|
||||
Note string `json:"note,omitempty"`
|
||||
SenderItemID string `json:"sender_item_id,omitempty"`
|
||||
RecipientType string `json:"recipient_type"`
|
||||
Receiver string `json:"receiver"`
|
||||
Amount *AmountPayout `json:"amount"`
|
||||
Note string `json:"note,omitempty"`
|
||||
SenderItemID string `json:"sender_item_id,omitempty"`
|
||||
}
|
||||
|
||||
// PayoutItemResponse struct
|
||||
|
@ -1150,7 +783,6 @@ type (
|
|||
CaptureID string `json:"capture_id,omitempty"`
|
||||
ParentPayment string `json:"parent_payment,omitempty"`
|
||||
UpdateTime *time.Time `json:"update_time,omitempty"`
|
||||
SaleID string `json:"sale_id,omitempty"`
|
||||
}
|
||||
|
||||
// RefundResponse .
|
||||
|
@ -1158,7 +790,6 @@ type (
|
|||
ID string `json:"id,omitempty"`
|
||||
Amount *PurchaseUnitAmount `json:"amount,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Links []Link `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
// Related struct
|
||||
|
@ -1198,7 +829,8 @@ type (
|
|||
|
||||
//ShippingAmount struct
|
||||
ShippingAmount struct {
|
||||
Money
|
||||
CurrencyCode string `json:"currency_code,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// ShippingAddress struct
|
||||
|
@ -1225,14 +857,8 @@ type (
|
|||
}
|
||||
|
||||
// Name struct
|
||||
//Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-name
|
||||
Name struct {
|
||||
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"`
|
||||
FullName string `json:"full_name,omitempty"`
|
||||
}
|
||||
|
||||
// ShippingDetail struct
|
||||
|
@ -1243,7 +869,6 @@ type (
|
|||
|
||||
// Subscriber struct
|
||||
Subscriber struct {
|
||||
PayerID string `json:"payer_id"`
|
||||
ShippingAddress ShippingDetail `json:"shipping_address,omitempty"`
|
||||
Name CreateOrderPayerName `json:"name,omitempty"`
|
||||
EmailAddress string `json:"email_address,omitempty"`
|
||||
|
@ -1350,11 +975,7 @@ type (
|
|||
VerificationStatus string `json:"verification_status,omitempty"`
|
||||
}
|
||||
|
||||
WebhookEventTypesResponse struct {
|
||||
EventTypes []WebhookEventType `json:"event_types"`
|
||||
}
|
||||
|
||||
// Webhook struct
|
||||
// Webhook strunct
|
||||
Webhook struct {
|
||||
ID string `json:"id"`
|
||||
URL string `json:"url"`
|
||||
|
@ -1362,31 +983,23 @@ type (
|
|||
Links []Link `json:"links"`
|
||||
}
|
||||
|
||||
// Event struct.
|
||||
//
|
||||
// The basic webhook event data type. This struct is intended to be
|
||||
// embedded into resource type specific event structs.
|
||||
Event struct {
|
||||
// WebhookEvent struct
|
||||
WebhookEvent struct {
|
||||
ID string `json:"id"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
EventType string `json:"event_type"`
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Resource Resource `json:"resource"`
|
||||
Links []Link `json:"links"`
|
||||
EventVersion string `json:"event_version,omitempty"`
|
||||
ResourceVersion string `json:"resource_version,omitempty"`
|
||||
}
|
||||
|
||||
AnyEvent struct {
|
||||
Event
|
||||
Resource json.RawMessage `json:"resource"`
|
||||
}
|
||||
|
||||
// WebhookEventType struct
|
||||
WebhookEventType struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// CreateWebhookRequest struct
|
||||
|
@ -1405,6 +1018,26 @@ type (
|
|||
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 {
|
||||
GrossAmount PurchaseUnitAmount `json:"gross_amount"`
|
||||
PayPalFee PurchaseUnitAmount `json:"paypal_fee"`
|
||||
|
@ -1420,10 +1053,6 @@ type (
|
|||
LegalConsents []Consent `json:"legal_consents,omitempty"`
|
||||
}
|
||||
|
||||
ReferralResponse struct {
|
||||
Links []Link `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
PartnerConfigOverride struct {
|
||||
PartnerLogoURL string `json:"partner_logo_url,omitempty"`
|
||||
ReturnURL string `json:"return_url,omitempty"`
|
||||
|
@ -1555,30 +1184,6 @@ type (
|
|||
ShippingInfo *SearchShippingInfo `json:"shipping_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
|
||||
|
@ -1613,9 +1218,3 @@ func (e *expirationTime) UnmarshalJSON(b []byte) error {
|
|||
*e = expirationTime(i)
|
||||
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 (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type webprofileTestServer struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
c, err := NewClient("", "", "")
|
||||
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) {
|
||||
response := `{
|
||||
"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) {
|
||||
response := `{
|
||||
"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))
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// StoreCreditCard func
|
||||
// Endpoint: POST /v1/vault/credit-cards
|
||||
func (c *Client) StoreCreditCard(ctx context.Context, cc CreditCard) (*CreditCard, error) {
|
||||
req, err := c.NewRequest(ctx, "POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/vault/credit-cards"), cc)
|
||||
func (c *Client) StoreCreditCard(cc CreditCard) (*CreditCard, error) {
|
||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/vault/credit-cards"), cc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -24,8 +23,8 @@ func (c *Client) StoreCreditCard(ctx context.Context, cc CreditCard) (*CreditCar
|
|||
|
||||
// DeleteCreditCard func
|
||||
// Endpoint: DELETE /v1/vault/credit-cards/credit_card_id
|
||||
func (c *Client) DeleteCreditCard(ctx context.Context, id string) error {
|
||||
req, err := c.NewRequest(ctx, "DELETE", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
||||
func (c *Client) DeleteCreditCard(id string) error {
|
||||
req, err := c.NewRequest("DELETE", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -39,8 +38,8 @@ func (c *Client) DeleteCreditCard(ctx context.Context, id string) error {
|
|||
|
||||
// GetCreditCard func
|
||||
// Endpoint: GET /v1/vault/credit-cards/credit_card_id
|
||||
func (c *Client) GetCreditCard(ctx context.Context, id string) (*CreditCard, error) {
|
||||
req, err := c.NewRequest(ctx, "GET", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
||||
func (c *Client) GetCreditCard(id string) (*CreditCard, error) {
|
||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -56,7 +55,7 @@ func (c *Client) GetCreditCard(ctx context.Context, id string) (*CreditCard, err
|
|||
|
||||
// GetCreditCards func
|
||||
// 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
|
||||
if ccf != nil && ccf.Page > 0 {
|
||||
page = ccf.Page
|
||||
|
@ -66,7 +65,7 @@ func (c *Client) GetCreditCards(ctx context.Context, ccf *CreditCardsFilter) (*C
|
|||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -82,8 +81,8 @@ func (c *Client) GetCreditCards(ctx context.Context, ccf *CreditCardsFilter) (*C
|
|||
|
||||
// PatchCreditCard func
|
||||
// Endpoint: PATCH /v1/vault/credit-cards/credit_card_id
|
||||
func (c *Client) PatchCreditCard(ctx context.Context, id string, ccf []CreditCardField) (*CreditCard, error) {
|
||||
req, err := c.NewRequest(ctx, "PATCH", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), ccf)
|
||||
func (c *Client) PatchCreditCard(id string, ccf []CreditCardField) (*CreditCard, error) {
|
||||
req, err := c.NewRequest("PATCH", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), ccf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
54
webhooks.go
54
webhooks.go
|
@ -2,18 +2,16 @@ package paypal
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CreateWebhook - Subscribes your webhook listener to events.
|
||||
// Endpoint: POST /v1/notifications/webhooks
|
||||
func (c *Client) CreateWebhook(ctx context.Context, createWebhookRequest *CreateWebhookRequest) (*Webhook, error) {
|
||||
req, err := c.NewRequest(ctx, http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), createWebhookRequest)
|
||||
func (c *Client) CreateWebhook(createWebhookRequest *CreateWebhookRequest) (*Webhook, error) {
|
||||
req, err := c.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), createWebhookRequest)
|
||||
webhook := &Webhook{}
|
||||
if err != nil {
|
||||
return webhook, err
|
||||
|
@ -25,8 +23,8 @@ func (c *Client) CreateWebhook(ctx context.Context, createWebhookRequest *Create
|
|||
|
||||
// GetWebhook - Shows details for a webhook, by ID.
|
||||
// Endpoint: GET /v1/notifications/webhooks/ID
|
||||
func (c *Client) GetWebhook(ctx context.Context, webhookID string) (*Webhook, error) {
|
||||
req, err := c.NewRequest(ctx, http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/notifications/webhooks/", webhookID), nil)
|
||||
func (c *Client) GetWebhook(webhookID string) (*Webhook, error) {
|
||||
req, err := c.NewRequest(http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/notifications/webhooks/", webhookID), nil)
|
||||
webhook := &Webhook{}
|
||||
if err != nil {
|
||||
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.
|
||||
// Endpoint: PATCH /v1/notifications/webhooks/ID
|
||||
func (c *Client) UpdateWebhook(ctx context.Context, webhookID string, fields []WebhookField) (*Webhook, error) {
|
||||
req, err := c.NewRequest(ctx, http.MethodPatch, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), fields)
|
||||
func (c *Client) UpdateWebhook(webhookID string, fields []WebhookField) (*Webhook, error) {
|
||||
req, err := c.NewRequest(http.MethodPatch, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), fields)
|
||||
webhook := &Webhook{}
|
||||
if err != nil {
|
||||
return webhook, err
|
||||
|
@ -51,11 +49,11 @@ func (c *Client) UpdateWebhook(ctx context.Context, webhookID string, fields []W
|
|||
|
||||
// ListWebhooks - Lists webhooks for an app.
|
||||
// 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 {
|
||||
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.Add("anchor_type", anchorType)
|
||||
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.
|
||||
// Endpoint: DELETE /v1/notifications/webhooks/ID
|
||||
func (c *Client) DeleteWebhook(ctx context.Context, webhookID string) error {
|
||||
req, err := c.NewRequest(ctx, http.MethodDelete, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), nil)
|
||||
func (c *Client) DeleteWebhook(webhookID string) error {
|
||||
req, err := c.NewRequest(http.MethodDelete, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), nil)
|
||||
if err != nil {
|
||||
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.
|
||||
// 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 {
|
||||
AuthAlgo string `json:"auth_algo,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"`
|
||||
TransmissionTime string `json:"transmission_time,omitempty"`
|
||||
WebhookID string `json:"webhook_id,omitempty"`
|
||||
Event json.RawMessage `json:"webhook_event,omitempty"`
|
||||
WebhookEvent json.RawMessage `json:"webhook_event"`
|
||||
}
|
||||
|
||||
// Read the content
|
||||
var bodyBytes []byte
|
||||
if httpReq.Body != nil {
|
||||
bodyBytes, _ = io.ReadAll(httpReq.Body)
|
||||
} else {
|
||||
return nil, errors.New("cannot verify webhook for HTTP Request with empty body")
|
||||
bodyBytes, _ = ioutil.ReadAll(httpReq.Body)
|
||||
}
|
||||
// Restore the io.ReadCloser to its original state
|
||||
httpReq.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
httpReq.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
|
||||
verifyRequest := verifyWebhookSignatureRequest{
|
||||
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"),
|
||||
TransmissionTime: httpReq.Header.Get("PAYPAL-TRANSMISSION-TIME"),
|
||||
WebhookID: webhookID,
|
||||
Event: json.RawMessage(bodyBytes),
|
||||
WebhookEvent: json.RawMessage(bodyBytes),
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -126,19 +122,3 @@ func (c *Client) VerifyWebhookSignature(ctx context.Context, httpReq *http.Reque
|
|||
|
||||
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
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
@ -11,9 +10,9 @@ import (
|
|||
// Allows for the customisation of the payment experience
|
||||
//
|
||||
// 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")
|
||||
req, err := c.NewRequest(ctx, "POST", url, wp)
|
||||
req, err := c.NewRequest("POST", url, wp)
|
||||
response := &WebProfile{}
|
||||
|
||||
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
|
||||
//
|
||||
// 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
|
||||
|
||||
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 {
|
||||
return &wp, err
|
||||
|
@ -51,14 +50,14 @@ func (c *Client) GetWebProfile(ctx context.Context, profileID string) (*WebProfi
|
|||
return &wp, nil
|
||||
}
|
||||
|
||||
// GetWebProfiles retrieves web experience profiles from Paypal
|
||||
// GetWebProfiles retreieves web experience profiles from Paypal
|
||||
//
|
||||
// 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
|
||||
|
||||
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 {
|
||||
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
|
||||
//
|
||||
// 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 == "" {
|
||||
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)
|
||||
|
||||
req, err := c.NewRequest(ctx, "PUT", url, wp)
|
||||
req, err := c.NewRequest("PUT", url, wp)
|
||||
|
||||
if err != nil {
|
||||
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
|
||||
//
|
||||
// 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)
|
||||
|
||||
req, err := c.NewRequest(ctx, "DELETE", url, nil)
|
||||
req, err := c.NewRequest("DELETE", url, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
Loading…
Reference in New Issue
Block a user