forked from go-packages/paypal
Subscriptions, Subscription plans and Products API (#151)
This commit is contained in:
parent
b3eb2c69dd
commit
d355a65df0
38
README.md
38
README.md
|
@ -20,11 +20,6 @@ Currently supports **v2** only, if you want to use **v1**, use **v1.1.4** git ta
|
||||||
* GET /v1/payment-experience/web-profiles/**ID**
|
* GET /v1/payment-experience/web-profiles/**ID**
|
||||||
* PUT /v1/payment-experience/web-profiles/**ID**
|
* PUT /v1/payment-experience/web-profiles/**ID**
|
||||||
* DELETE /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**
|
* GET /v2/payments/authorizations/**ID**
|
||||||
* POST /v2/payments/authorizations/**ID**/capture
|
* POST /v2/payments/authorizations/**ID**/capture
|
||||||
* POST /v2/payments/authorizations/**ID**/void
|
* POST /v2/payments/authorizations/**ID**/void
|
||||||
|
@ -32,23 +27,54 @@ Currently supports **v2** only, if you want to use **v1**, use **v1.1.4** git ta
|
||||||
* GET /v1/payments/sale/**ID**
|
* GET /v1/payments/sale/**ID**
|
||||||
* POST /v1/payments/sale/**ID**/refund
|
* POST /v1/payments/sale/**ID**/refund
|
||||||
* GET /v2/payments/refund/**ID**
|
* GET /v2/payments/refund/**ID**
|
||||||
|
* POST /v1/reporting/transactions
|
||||||
|
#Vault
|
||||||
|
* 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
|
||||||
|
#Checkout
|
||||||
* POST /v2/checkout/orders
|
* POST /v2/checkout/orders
|
||||||
* GET /v2/checkout/orders/**ID**
|
* GET /v2/checkout/orders/**ID**
|
||||||
* PATCH /v2/checkout/orders/**ID**
|
* PATCH /v2/checkout/orders/**ID**
|
||||||
* POST /v2/checkout/orders/**ID**/authorize
|
* POST /v2/checkout/orders/**ID**/authorize
|
||||||
* POST /v2/checkout/orders/**ID**/capture
|
* POST /v2/checkout/orders/**ID**/capture
|
||||||
|
#Billing plans (payments)
|
||||||
* GET /v1/payments/billing-plans
|
* GET /v1/payments/billing-plans
|
||||||
* POST /v1/payments/billing-plans
|
* POST /v1/payments/billing-plans
|
||||||
* PATCH /v1/payments/billing-plans/***ID***
|
* PATCH /v1/payments/billing-plans/***ID***
|
||||||
* POST /v1/payments/billing-agreements
|
* POST /v1/payments/billing-agreements
|
||||||
* POST /v1/payments/billing-agreements/***TOKEN***/agreement-execute
|
* POST /v1/payments/billing-agreements/***TOKEN***/agreement-execute
|
||||||
|
#Notifications
|
||||||
* POST /v1/notifications/webhooks
|
* POST /v1/notifications/webhooks
|
||||||
* GET /v1/notifications/webhooks
|
* GET /v1/notifications/webhooks
|
||||||
* GET /v1/notifications/webhooks/**ID**
|
* GET /v1/notifications/webhooks/**ID**
|
||||||
* PATCH /v1/notifications/webhooks/**ID**
|
* PATCH /v1/notifications/webhooks/**ID**
|
||||||
* DELETE /v1/notifications/webhooks/**ID**
|
* DELETE /v1/notifications/webhooks/**ID**
|
||||||
* POST /v1/notifications/verify-webhook-signature
|
* POST /v1/notifications/verify-webhook-signature
|
||||||
* POST /v1/reporting/transactions
|
#Products (Catalog)
|
||||||
|
* POST /v1/catalogs/products
|
||||||
|
* PATCH /v1/catalogs/products/**ID**
|
||||||
|
* GET /v1/catalogs/products/**ID**
|
||||||
|
* GET /v1/catalogs/products
|
||||||
|
#Billing Plans (Subscriptions)
|
||||||
|
* POST /v1/billing/plans
|
||||||
|
* PATCH /v1/billing/plans/**ID**
|
||||||
|
* GET /v1/billing/plans/**ID**
|
||||||
|
* GET /v1/billing/plans
|
||||||
|
* POST /v1/billing/plans/**ID**/activate
|
||||||
|
* POST /v1/billing/plans/**ID**/deactivate
|
||||||
|
* POST /v1/billing/plans/**ID**/update-pricing-schemes
|
||||||
|
#Subscriptions
|
||||||
|
* POST /v1/billing/subscriptions
|
||||||
|
* PATCH /v1/billing/subscriptions/**ID**
|
||||||
|
* GET /v1/billing/subscriptions/**ID**
|
||||||
|
* POST /v1/billing/subscriptions/**ID**/activate
|
||||||
|
* POST /v1/billing/subscriptions/**ID**/cancel
|
||||||
|
* POST /v1/billing/subscriptions/**ID**/capture
|
||||||
|
* POST /v1/billing/subscriptions/**ID**/suspend
|
||||||
|
* GET /v1/billing/subscriptions/**ID**/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 SDK Client, but you can use built-in **paypal** functions to perform a request: **NewClient -> NewRequest -> SendWithAuth**
|
||||||
|
|
59
billing.go
59
billing.go
|
@ -2,6 +2,7 @@ package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -31,26 +32,21 @@ type (
|
||||||
|
|
||||||
// BillingPlanListParams struct
|
// BillingPlanListParams struct
|
||||||
BillingPlanListParams struct {
|
BillingPlanListParams struct {
|
||||||
Page string `json:"page,omitempty"` //Default: 0.
|
ListParams
|
||||||
Status string `json:"status,omitempty"` //Allowed values: CREATED, ACTIVE, INACTIVE, ALL.
|
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
|
||||||
BillingPlanListResp struct {
|
BillingPlanListResp struct {
|
||||||
|
SharedListResponse
|
||||||
Plans []BillingPlan `json:"plans,omitempty"`
|
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
|
// CreateBillingPlan creates a billing plan in Paypal
|
||||||
// Endpoint: POST /v1/payments/billing-plans
|
// Endpoint: POST /v1/payments/billing-plans
|
||||||
func (c *Client) CreateBillingPlan(plan BillingPlan) (*CreateBillingResp, error) {
|
func (c *Client) CreateBillingPlan(plan BillingPlan) (*CreateBillingResp, error) {
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans"), plan)
|
req, err := c.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans"), plan)
|
||||||
response := &CreateBillingResp{}
|
response := &CreateBillingResp{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
|
@ -59,18 +55,35 @@ func (c *Client) CreateBillingPlan(plan BillingPlan) (*CreateBillingResp, error)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateBillingPlan updates values inside a billing plan
|
||||||
|
// Endpoint: PATCH /v1/payments/billing-plans
|
||||||
|
func (c *Client) UpdateBillingPlan(planId string, pathValues map[string]map[string]interface{}) error {
|
||||||
|
patchData := []Patch{}
|
||||||
|
for path, data := range pathValues {
|
||||||
|
patchData = append(patchData, Patch{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: path,
|
||||||
|
Value: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(patchData)
|
||||||
|
buf := bytes.NewBuffer(jsonData)
|
||||||
|
req, err := c.NewRequest(http.MethodPatch, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/payments/billing-plans/", planId), buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// ActivatePlan activates a billing plan
|
// ActivatePlan activates a billing plan
|
||||||
// By default, a new plan is not activated
|
// By default, a new plan is not activated
|
||||||
// Endpoint: PATCH /v1/payments/billing-plans/
|
// Endpoint: PATCH /v1/payments/billing-plans/
|
||||||
func (c *Client) ActivatePlan(planID string) error {
|
func (c *Client) ActivatePlan(planID string) error {
|
||||||
buf := bytes.NewBuffer([]byte(`[{"op":"replace","path":"/","value":{"state":"ACTIVE"}}]`))
|
return c.UpdateBillingPlan(planID, map[string]map[string]interface{}{
|
||||||
req, err := http.NewRequest("PATCH", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans/"+planID), buf)
|
"/": {"state": BillingPlanStatusActive},
|
||||||
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
|
// CreateBillingAgreement creates an agreement for specified plan
|
||||||
|
@ -81,7 +94,7 @@ func (c *Client) CreateBillingAgreement(a BillingAgreement) (*CreateAgreementRes
|
||||||
ID: a.Plan.ID,
|
ID: a.Plan.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-agreements"), a)
|
req, err := c.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-agreements"), a)
|
||||||
response := &CreateAgreementResp{}
|
response := &CreateAgreementResp{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
|
@ -93,7 +106,7 @@ func (c *Client) CreateBillingAgreement(a BillingAgreement) (*CreateAgreementRes
|
||||||
// ExecuteApprovedAgreement - Use this call to execute (complete) a PayPal agreement that has been approved by the payer.
|
// 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
|
// Endpoint: POST /v1/payments/billing-agreements/token/agreement-execute
|
||||||
func (c *Client) ExecuteApprovedAgreement(token string) (*ExecuteAgreementResponse, error) {
|
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)
|
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/v1/payments/billing-agreements/%s/agreement-execute", c.APIBase, token), nil)
|
||||||
response := &ExecuteAgreementResponse{}
|
response := &ExecuteAgreementResponse{}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -118,16 +131,18 @@ func (c *Client) ExecuteApprovedAgreement(token string) (*ExecuteAgreementRespon
|
||||||
// Endpoint: GET /v1/payments/billing-plans
|
// Endpoint: GET /v1/payments/billing-plans
|
||||||
func (c *Client) ListBillingPlans(bplp BillingPlanListParams) (*BillingPlanListResp, error) {
|
func (c *Client) ListBillingPlans(bplp BillingPlanListParams) (*BillingPlanListResp, error) {
|
||||||
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans"), nil)
|
req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/billing-plans"), nil)
|
||||||
|
response := &BillingPlanListResp{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
q := req.URL.Query()
|
q := req.URL.Query()
|
||||||
q.Add("page", bplp.Page)
|
q.Add("page", bplp.Page)
|
||||||
q.Add("page_size", bplp.PageSize)
|
q.Add("page_size", bplp.PageSize)
|
||||||
q.Add("status", bplp.Status)
|
q.Add("status", bplp.Status)
|
||||||
q.Add("total_required", bplp.TotalRequired)
|
q.Add("total_required", bplp.TotalRequired)
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
response := &BillingPlanListResp{}
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
err = c.SendWithAuth(req, response)
|
err = c.SendWithAuth(req, response)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
79
const.go
Normal file
79
const.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
)
|
2
go.mod
2
go.mod
|
@ -1,3 +1,5 @@
|
||||||
module github.com/plutov/paypal/v3
|
module github.com/plutov/paypal/v3
|
||||||
|
|
||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.6.0
|
||||||
|
|
11
go.sum
11
go.sum
|
@ -0,0 +1,11 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
|
||||||
|
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
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.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -3,7 +3,9 @@
|
||||||
package paypal
|
package paypal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// All test values are defined here
|
// All test values are defined here
|
||||||
|
@ -12,6 +14,9 @@ var testSecret = "EBoIiUSkCKeSk49hHSgTem1qnjzzJgRQHDEHvGpzlLEf_nIoJd91xu8rPOBDCd
|
||||||
var testUserID = "https://www.paypal.com/webapps/auth/identity/user/VBqgHcgZwb1PBs69ybjjXfIW86_Hr93aBvF_Rgbh2II"
|
var testUserID = "https://www.paypal.com/webapps/auth/identity/user/VBqgHcgZwb1PBs69ybjjXfIW86_Hr93aBvF_Rgbh2II"
|
||||||
var testCardID = "CARD-54E6956910402550WKGRL6EA"
|
var testCardID = "CARD-54E6956910402550WKGRL6EA"
|
||||||
|
|
||||||
|
var testProductId = "" // will be fetched in func TestProduct(t *testing.T)
|
||||||
|
var testBillingPlan = "" // will be fetched in func TestSubscriptionPlans(t *testing.T)
|
||||||
|
|
||||||
func TestGetAccessToken(t *testing.T) {
|
func TestGetAccessToken(t *testing.T) {
|
||||||
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
||||||
token, err := c.GetAccessToken()
|
token, err := c.GetAccessToken()
|
||||||
|
@ -212,3 +217,155 @@ func TestListWebhooks(t *testing.T) {
|
||||||
t.Errorf("Cannot registered list webhooks, error %v", err)
|
t.Errorf("Cannot registered list webhooks, error %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProduct(t *testing.T) {
|
||||||
|
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
||||||
|
c.GetAccessToken()
|
||||||
|
|
||||||
|
//create a product
|
||||||
|
productData := Product{
|
||||||
|
Name: "Test Product",
|
||||||
|
Description: "A Test Product",
|
||||||
|
Category: PRODUCT_CATEGORY_SOFTWARE,
|
||||||
|
Type: PRODUCT_TYPE_SERVICE,
|
||||||
|
ImageUrl: "https://example.com/image.png",
|
||||||
|
HomeUrl: "https://example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
productCreateResponse, err := c.CreateProduct(productData)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
testProductId = productCreateResponse.ID
|
||||||
|
|
||||||
|
//update the product
|
||||||
|
productData.ID = productCreateResponse.ID
|
||||||
|
productData.Description = "Updated product"
|
||||||
|
|
||||||
|
err = c.UpdateProduct(productData)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
//get product data
|
||||||
|
productFetched, err := c.GetProduct(productData.ID)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, productFetched.Description, "Updated product")
|
||||||
|
|
||||||
|
//test that lising products have more than one product
|
||||||
|
productList, err := c.ListProducts(nil)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.NotEqual(t, len(productList.Products), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubscriptionPlans(t *testing.T) {
|
||||||
|
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
||||||
|
c.GetAccessToken()
|
||||||
|
|
||||||
|
//create a product
|
||||||
|
newSubscriptionPlan := SubscriptionPlan{
|
||||||
|
ProductId: testProductId,
|
||||||
|
Name: "Test subscription plan",
|
||||||
|
Status: SubscriptionPlanStatusCreated,
|
||||||
|
Description: "Integration test subscription plan",
|
||||||
|
BillingCycles: []BillingCycle{
|
||||||
|
{
|
||||||
|
PricingScheme: PricingScheme{
|
||||||
|
Version: 1,
|
||||||
|
FixedPrice: Money{
|
||||||
|
Currency: "EUR",
|
||||||
|
Value: "5",
|
||||||
|
},
|
||||||
|
CreateTime: time.Now(),
|
||||||
|
UpdateTime: time.Now(),
|
||||||
|
},
|
||||||
|
Frequency: Frequency{
|
||||||
|
IntervalUnit: IntervalUnitYear,
|
||||||
|
IntervalCount: 1,
|
||||||
|
},
|
||||||
|
TenureType: TenureTypeRegular,
|
||||||
|
Sequence: 1,
|
||||||
|
TotalCycles: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PaymentPreferences: PaymentPreferences{
|
||||||
|
AutoBillOutstanding: false,
|
||||||
|
SetupFee: nil,
|
||||||
|
SetupFeeFailureAction: SetupFeeFailureActionCancel,
|
||||||
|
PaymentFailureThreshold: 0,
|
||||||
|
},
|
||||||
|
Taxes: Taxes{
|
||||||
|
Percentage: "19",
|
||||||
|
Inclusive: false,
|
||||||
|
},
|
||||||
|
QuantitySupported: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
//test create new plan
|
||||||
|
planCreateResponse, err := c.CreateSubscriptionPlan(newSubscriptionPlan)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
testBillingPlan = planCreateResponse.ID // for next test
|
||||||
|
|
||||||
|
//test update the newly created plan
|
||||||
|
newSubscriptionPlan.ID = planCreateResponse.ID
|
||||||
|
newSubscriptionPlan.Description = "updated description"
|
||||||
|
err = c.UpdateSubscriptionPlan(newSubscriptionPlan)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
//test get plan information
|
||||||
|
existingPlan, err := c.GetSubscriptionPlan(newSubscriptionPlan.ID)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, newSubscriptionPlan.Description, existingPlan.Description)
|
||||||
|
|
||||||
|
//test activate plan
|
||||||
|
err = c.ActivateSubscriptionPlan(newSubscriptionPlan.ID)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
//test deactivate plan
|
||||||
|
err = c.DeactivateSubscriptionPlans(newSubscriptionPlan.ID)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
//reactivate this plan for next next (subscription)
|
||||||
|
err = c.ActivateSubscriptionPlan(newSubscriptionPlan.ID)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
//test upadte plan pricing
|
||||||
|
err = c.UpdateSubscriptionPlanPricing(newSubscriptionPlan.ID, []PricingSchemeUpdate{
|
||||||
|
{
|
||||||
|
BillingCycleSequence: 1,
|
||||||
|
PricingScheme: PricingScheme{
|
||||||
|
Version: 1,
|
||||||
|
FixedPrice: Money{
|
||||||
|
Currency: "EUR",
|
||||||
|
Value: "6",
|
||||||
|
},
|
||||||
|
CreateTime: time.Now(),
|
||||||
|
UpdateTime: time.Now(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
|
||||||
|
//test update pricing scheme
|
||||||
|
updatedPricingPlan, err := c.GetSubscriptionPlan(newSubscriptionPlan.ID)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.Equal(t, "6.0", updatedPricingPlan.BillingCycles[0].PricingScheme.FixedPrice.Value)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubscription(t *testing.T) {
|
||||||
|
c, _ := NewClient(testClientID, testSecret, APIBaseSandBox)
|
||||||
|
c.GetAccessToken()
|
||||||
|
|
||||||
|
newSubscription := SubscriptionBase{
|
||||||
|
PlanID: testBillingPlan,
|
||||||
|
}
|
||||||
|
|
||||||
|
//create new subscription
|
||||||
|
newSubResponse, err := c.CreateSubscription(newSubscription)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.NotEqual(t, "", newSubResponse.ID)
|
||||||
|
|
||||||
|
//get subscription details
|
||||||
|
subDetails, err := c.GetSubscriptionDetails(newSubResponse.ID)
|
||||||
|
assert.Equal(t, nil, err)
|
||||||
|
assert.NotEqual(t, "", subDetails.ID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
7
patch.go
Normal file
7
patch.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package paypal
|
||||||
|
|
||||||
|
type Patch struct {
|
||||||
|
Operation string `json:"op"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
}
|
129
products.go
Normal file
129
products.go
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
package paypal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
ProductType string
|
||||||
|
ProductCategory string
|
||||||
|
|
||||||
|
// Product struct
|
||||||
|
Product struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Category ProductCategory `json:"category,omitempty"`
|
||||||
|
Type ProductType `json:"type"`
|
||||||
|
ImageUrl string `json:"image_url"`
|
||||||
|
HomeUrl string `json:"home_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateProductResponse struct {
|
||||||
|
Product
|
||||||
|
SharedResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
ListProductsResponse struct {
|
||||||
|
Products []Product `json:"products"`
|
||||||
|
SharedListResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
ProductListParameters struct {
|
||||||
|
ListParams
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *Product) GetUpdatePatch() []Patch {
|
||||||
|
return []Patch{
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/description",
|
||||||
|
Value: self.Description,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/category",
|
||||||
|
Value: self.Category,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/image_url",
|
||||||
|
Value: self.ImageUrl,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/home_url",
|
||||||
|
Value: self.HomeUrl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PRODUCT_TYPE_PHYSICAL ProductType = "PHYSICAL"
|
||||||
|
PRODUCT_TYPE_DIGITAL ProductType = "DIGITAL"
|
||||||
|
PRODUCT_TYPE_SERVICE ProductType = "SERVICE"
|
||||||
|
|
||||||
|
PRODUCT_CATEGORY_SOFTWARE ProductCategory = "software"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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(product Product) (*CreateProductResponse, error) {
|
||||||
|
req, err := c.NewRequest(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(product Product) error {
|
||||||
|
req, err := c.NewRequest(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(productId string) (*Product, error) {
|
||||||
|
req, err := c.NewRequest(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(params *ProductListParameters) (*ListProductsResponse, error) {
|
||||||
|
req, err := c.NewRequest(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
|
||||||
|
}
|
176
subscription.go
176
subscription.go
|
@ -7,27 +7,110 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// SubscriptionDetailResp struct
|
SubscriptionBase struct {
|
||||||
SubscriptionDetailResp struct {
|
PlanID string `json:"plan_id"`
|
||||||
ID string `json:"id,omitempty"`
|
StartTime *JSONTime `json:"start_time,omitempty"`
|
||||||
PlanID string `json:"plan_id,omitempty"`
|
|
||||||
StartTime time.Time `json:"start_time,omitempty"`
|
|
||||||
Quantity string `json:"quantity,omitempty"`
|
Quantity string `json:"quantity,omitempty"`
|
||||||
ShippingAmount ShippingAmount `json:"shipping_amount,omitempty"`
|
ShippingAmount *Money `json:"shipping_amount,omitempty"`
|
||||||
Subscriber Subscriber `json:"subscriber,omitempty"`
|
Subscriber *Subscriber `json:"subscriber,omitempty"`
|
||||||
BillingInfo BillingInfo `json:"billing_info,omitempty"`
|
AutoRenewal bool `json:"auto_renewal,omitempty"`
|
||||||
CreateTime time.Time `json:"create_time,omitempty"`
|
ApplicationContext *ApplicationContext `json:"application_context,omitempty"`
|
||||||
UpdateTime time.Time `json:"update_time,omitempty"`
|
}
|
||||||
Links []Link `json:"links,omitempty"`
|
|
||||||
Status string `json:"status,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"`
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptureReqeust struct {
|
||||||
|
Note string `json:"note"`
|
||||||
|
CaptureType CaptureType `json:"capture_type"`
|
||||||
|
Amount Money `json:"amount"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (self *Subscription) GetUpdatePatch() []Patch {
|
||||||
|
result := []Patch{
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/billing_info/outstanding_balance",
|
||||||
|
Value: self.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(newSubscription SubscriptionBase) (*Subscription, error) {
|
||||||
|
req, err := c.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/billing/subscriptions"), newSubscription)
|
||||||
|
response := &Subscription{}
|
||||||
|
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(updatedSubscription Subscription) error {
|
||||||
|
req, err := c.NewRequest(http.MethodPatch, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/billing/subscriptions/", updatedSubscription.ID), updatedSubscription.GetUpdatePatch())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.SendWithAuth(req, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// GetSubscriptionDetails shows details for a subscription, by ID.
|
// GetSubscriptionDetails shows details for a subscription, by ID.
|
||||||
// Endpoint: GET /v1/billing/subscriptions/
|
// Endpoint: GET /v1/billing/subscriptions/
|
||||||
func (c *Client) GetSubscriptionDetails(subscriptionID string) (*SubscriptionDetailResp, error) {
|
func (c *Client) GetSubscriptionDetails(subscriptionID string) (*SubscriptionDetailResp, error) {
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v1/billing/subscriptions/%s", c.APIBase, subscriptionID), nil)
|
||||||
response := &SubscriptionDetailResp{}
|
response := &SubscriptionDetailResp{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return response, err
|
||||||
|
@ -35,3 +118,70 @@ func (c *Client) GetSubscriptionDetails(subscriptionID string) (*SubscriptionDet
|
||||||
err = c.SendWithAuth(req, response)
|
err = c.SendWithAuth(req, response)
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Activates the subscription.
|
||||||
|
// Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_activate
|
||||||
|
// Endpoint: POST /v1/billing/subscriptions/{id}/activate
|
||||||
|
func (c *Client) ActivateSubscription(subscriptionId, activateReason string) error {
|
||||||
|
req, err := c.NewRequest(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(subscriptionId, cancelReason string) error {
|
||||||
|
req, err := c.NewRequest(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(subscriptionId string, request CaptureReqeust) (*SubscriptionCaptureResponse, error) {
|
||||||
|
req, err := c.NewRequest(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(subscriptionId, reason string) error {
|
||||||
|
req, err := c.NewRequest(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(requestParams SubscriptionTransactionsParams) (*SubscriptionTransactionsResponse, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v1/billing/subscriptions/%s/transactions", c.APIBase, requestParams.SubscriptionId), nil)
|
||||||
|
response := &SubscriptionTransactionsResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("start_time", requestParams.StartTime.Format(time.RFC3339Nano))
|
||||||
|
q.Add("end_time", requestParams.EndTime.Format(time.RFC3339Nano))
|
||||||
|
|
||||||
|
err = c.SendWithAuth(req, response)
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
226
subscription_plan.go
Normal file
226
subscription_plan.go
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
package paypal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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 SubscriptionPlanStatus `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 (self *SubscriptionPlan) GetUpdatePatch() []Patch {
|
||||||
|
result := []Patch{
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/description",
|
||||||
|
Value: self.Description,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/auto_bill_outstanding",
|
||||||
|
Value: self.PaymentPreferences.AutoBillOutstanding,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/payment_failure_threshold",
|
||||||
|
Value: self.PaymentPreferences.PaymentFailureThreshold,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/setup_fee_failure_action",
|
||||||
|
Value: self.PaymentPreferences.SetupFeeFailureAction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/taxes/percentage",
|
||||||
|
Value: self.Taxes.Percentage,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.PaymentPreferences.SetupFee != nil {
|
||||||
|
result = append(result, Patch{
|
||||||
|
Operation: "replace",
|
||||||
|
Path: "/payment_preferences/setup_fee",
|
||||||
|
Value: self.PaymentPreferences.SetupFee,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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(newPlan SubscriptionPlan) (*CreateSubscriptionPlanResponse, error) {
|
||||||
|
req, err := c.NewRequest(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(updatedPlan SubscriptionPlan) error {
|
||||||
|
req, err := c.NewRequest(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(planId string) (*SubscriptionPlan, error) {
|
||||||
|
req, err := c.NewRequest(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(params *SubscriptionPlanListParameters) (*ListProductsResponse, error) {
|
||||||
|
req, err := c.NewRequest(http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/billing/plans"), 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)
|
||||||
|
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(planId string) error {
|
||||||
|
req, err := c.NewRequest(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(planId string) error {
|
||||||
|
req, err := c.NewRequest(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(planId string, pricingSchemes []PricingSchemeUpdate) error {
|
||||||
|
req, err := c.NewRequest(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
|
||||||
|
}
|
|
@ -30,9 +30,7 @@ type TransactionSearchResponse struct {
|
||||||
EndDate JSONTime `json:"end_date"`
|
EndDate JSONTime `json:"end_date"`
|
||||||
LastRefreshDatetime JSONTime `json:"last_refreshed_datetime"`
|
LastRefreshDatetime JSONTime `json:"last_refreshed_datetime"`
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
TotalItems int `json:"total_items"`
|
SharedListResponse
|
||||||
TotalPages int `json:"total_pages"`
|
|
||||||
Links []Link `json:"links"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTransactions - Use this to search PayPal transactions from the last 31 days.
|
// ListTransactions - Use this to search PayPal transactions from the last 31 days.
|
||||||
|
|
43
types.go
43
types.go
|
@ -85,11 +85,6 @@ const (
|
||||||
// Possible values for `shipping_preference` in ApplicationContext
|
// Possible values for `shipping_preference` in ApplicationContext
|
||||||
//
|
//
|
||||||
// https://developer.paypal.com/docs/api/orders/v2/#definition-application_context
|
// https://developer.paypal.com/docs/api/orders/v2/#definition-application_context
|
||||||
const (
|
|
||||||
ShippingPreferenceGetFromFile string = "GET_FROM_FILE"
|
|
||||||
ShippingPreferenceNoShipping string = "NO_SHIPPING"
|
|
||||||
ShippingPreferenceSetProvidedAddress string = "SET_PROVIDED_ADDRESS"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EventPaymentCaptureCompleted string = "PAYMENT.CAPTURE.COMPLETED"
|
EventPaymentCaptureCompleted string = "PAYMENT.CAPTURE.COMPLETED"
|
||||||
|
@ -171,12 +166,13 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplicationContext struct
|
// ApplicationContext struct
|
||||||
|
//Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-application_context
|
||||||
ApplicationContext struct {
|
ApplicationContext struct {
|
||||||
BrandName string `json:"brand_name,omitempty"`
|
BrandName string `json:"brand_name,omitempty"`
|
||||||
Locale string `json:"locale,omitempty"`
|
Locale string `json:"locale,omitempty"`
|
||||||
LandingPage string `json:"landing_page,omitempty"`
|
ShippingPreference ShippingPreference `json:"shipping_preference,omitempty"`
|
||||||
ShippingPreference string `json:"shipping_preference,omitempty"`
|
UserAction UserAction `json:"user_action,omitempty"`
|
||||||
UserAction string `json:"user_action,omitempty"`
|
//LandingPage string `json:"landing_page,omitempty"` // not found in documentation
|
||||||
ReturnURL string `json:"return_url,omitempty"`
|
ReturnURL string `json:"return_url,omitempty"`
|
||||||
CancelURL string `json:"cancel_url,omitempty"`
|
CancelURL string `json:"cancel_url,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -359,9 +355,7 @@ type (
|
||||||
// CreditCards GET /v1/vault/credit-cards
|
// CreditCards GET /v1/vault/credit-cards
|
||||||
CreditCards struct {
|
CreditCards struct {
|
||||||
Items []CreditCard `json:"items"`
|
Items []CreditCard `json:"items"`
|
||||||
Links []Link `json:"links"`
|
SharedListResponse
|
||||||
TotalItems int `json:"total_items"`
|
|
||||||
TotalPages int `json:"total_pages"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreditCardToken struct
|
// CreditCardToken struct
|
||||||
|
@ -829,8 +823,7 @@ type (
|
||||||
|
|
||||||
//ShippingAmount struct
|
//ShippingAmount struct
|
||||||
ShippingAmount struct {
|
ShippingAmount struct {
|
||||||
CurrencyCode string `json:"currency_code,omitempty"`
|
Money
|
||||||
Value string `json:"value,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShippingAddress struct
|
// ShippingAddress struct
|
||||||
|
@ -857,8 +850,14 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name struct
|
// Name struct
|
||||||
|
//Doc: https://developer.paypal.com/docs/api/subscriptions/v1/#definition-name
|
||||||
Name struct {
|
Name struct {
|
||||||
FullName string `json:"full_name,omitempty"`
|
FullName string `json:"full_name,omitempty"`
|
||||||
|
Suffix string `json:"suffix,omitempty"`
|
||||||
|
Prefix string `json:"prefix,omitempty"`
|
||||||
|
GivenName string `json:"given_name,omitempty"`
|
||||||
|
Surname string `json:"surname,omitempty"`
|
||||||
|
MiddleName string `json:"middle_name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShippingDetail struct
|
// ShippingDetail struct
|
||||||
|
@ -1184,6 +1183,24 @@ type (
|
||||||
ShippingInfo *SearchShippingInfo `json:"shipping_info"`
|
ShippingInfo *SearchShippingInfo `json:"shipping_info"`
|
||||||
CartInfo *SearchCartInfo `json:"cart_info"`
|
CartInfo *SearchCartInfo `json:"cart_info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedResponse struct {
|
||||||
|
CreateTime string `json:"create_time"`
|
||||||
|
UpdateTime string `json:"update_time"`
|
||||||
|
Links []Link `json:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
ListParams struct {
|
||||||
|
Page string `json:"page,omitempty"` //Default: 0.
|
||||||
|
PageSize string `json:"page_size,omitempty"` //Default: 10.
|
||||||
|
TotalRequired string `json:"total_required,omitempty"` //Default: no.
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedListResponse struct {
|
||||||
|
TotalItems string `json:"total_items,omitempty"`
|
||||||
|
TotalPages string `json:"total_pages,omitempty"`
|
||||||
|
Links []Link `json:"links,omitempty"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error method implementation for ErrorResponse struct
|
// Error method implementation for ErrorResponse struct
|
||||||
|
|
Loading…
Reference in New Issue
Block a user