Merge pull request #32 from logpacker/feature_billing_plan

Feature billing plan
This commit is contained in:
envy124 2017-09-08 14:26:30 +03:00 committed by GitHub
commit d30e9ced1e
4 changed files with 291 additions and 6 deletions

View File

@ -35,6 +35,10 @@
* PATCH /v1/vault/credit-cards/**ID** * PATCH /v1/vault/credit-cards/**ID**
* GET /v1/vault/credit-cards/**ID** * GET /v1/vault/credit-cards/**ID**
* GET /v1/vault/credit-cards * GET /v1/vault/credit-cards
* POST /v1/payments/billing-plans
* PATCH /v1/payments/billing-plans/***ID***
* POST /v1/payments/billing-agreements
* POST /v1/payments/billing-agreements/***TOKEN***/agreement-execute
### Missing endpoints ### Missing endpoints
It is possible that some endpoints are missing in this SDK Client, but you can use built-in **paypalsdk** 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 **paypalsdk** functions to perform a request: **NewClient -> NewRequest -> SendWithAuth**

93
billing.go Normal file
View File

@ -0,0 +1,93 @@
package paypalsdk
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"`
}
)
// 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
}
// 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)
}
// Creates an agreement for specified plan
// Endpoint: POST /v1/payments/billing-agreements
func (c *Client) CreateBillingAgreement(a BillingAgreement) (*CreateAgreementResp, error) {
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)
if err != nil {
return &ExecuteAgreementResponse{}, err
}
req.SetBasicAuth(c.ClientID, c.Secret)
req.Header.Set("Authorization", "Bearer "+c.Token.Token)
e := ExecuteAgreementResponse{}
err = c.SendWithAuth(req, &e)
if err != nil {
return &e, err
}
if e.ID == "" {
return &e, errors.New("Unable to execute agreement with token=" + token)
}
return &e, err
}

107
billing_test.go Normal file
View File

@ -0,0 +1,107 @@
package paypalsdk_test
import (
"fmt"
pp "github.com/logpacker/PayPal-Go-SDK"
"time"
)
func BillingExample() {
plan := pp.BillingPlan{
Name: "Plan with Regular and Trial Payment Definitions",
Description: "Plan with regular and trial payment definitions.",
Type: "fixed",
PaymentDefinitions: []pp.PaymentDefinition{
pp.PaymentDefinition{
Name: "Regular payment definition",
Type: "REGULAR",
Frequency: "MONTH",
FrequencyInterval: "2",
Amount: pp.AmountPayout{
Value: "100",
Currency: "USD",
},
Cycles: "12",
ChargeModels: []pp.ChargeModel{
pp.ChargeModel{
Type: "SHIPPING",
Amount: pp.AmountPayout{
Value: "10",
Currency: "USD",
},
},
pp.ChargeModel{
Type: "TAX",
Amount: pp.AmountPayout{
Value: "12",
Currency: "USD",
},
},
},
},
pp.PaymentDefinition{
Name: "Trial payment definition",
Type: "trial",
Frequency: "week",
FrequencyInterval: "5",
Amount: pp.AmountPayout{
Value: "9.19",
Currency: "USD",
},
Cycles: "2",
ChargeModels: []pp.ChargeModel{
pp.ChargeModel{
Type: "SHIPPING",
Amount: pp.AmountPayout{
Value: "1",
Currency: "USD",
},
},
pp.ChargeModel{
Type: "TAX",
Amount: pp.AmountPayout{
Value: "2",
Currency: "USD",
},
},
},
},
},
MerchantPreferences: &pp.MerchantPreferences{
SetupFee: &pp.AmountPayout{
Value: "1",
Currency: "USD",
},
ReturnUrl: "http://www.paypal.com",
CancelUrl: "http://www.paypal.com/cancel",
AutoBillAmount: "YES",
InitialFailAmountAction: "CONTINUE",
MaxFailAttempts: "0",
},
}
c, err := pp.NewClient("clientID", "secretID", pp.APIBaseSandBox)
if err != nil {
panic(err)
}
_, err = c.GetAccessToken()
if err != nil {
panic(err)
}
planResp, err := c.CreateBillingPlan(plan)
if err != nil {
panic(err)
}
err = c.ActivatePlan(planResp.ID)
fmt.Println(err)
agreement := pp.BillingAgreement{
Name: "Fast Speed Agreement",
Description: "Agreement for Fast Speed Plan",
StartDate: pp.JsonTime(time.Now().Add(time.Hour * 24)),
Plan: pp.BillingPlan{ID: planResp.ID},
Payer: pp.Payer{
PaymentMethod: "paypal",
},
}
resp, err := c.CreateBillingAgreement(agreement)
fmt.Println(err, resp)
}

View File

@ -44,6 +44,9 @@ const (
) )
type ( type (
// JsonTime overrides MarshalJson method to format in ISO8601
JsonTime time.Time
// Address struct // Address struct
Address struct { Address struct {
Line1 string `json:"line1"` Line1 string `json:"line1"`
@ -55,6 +58,18 @@ type (
Phone string `json:"phone,omitempty"` Phone string `json:"phone,omitempty"`
} }
// AgreementDetails struct
AgreementDetails struct {
OutstandingBalance AmountPayout `json:"outstanding_balance"`
CyclesRemaining int `json:"cycles_remaining,string"`
CyclesCompleted int `json:"cycles_completed,string"`
NextBillingDate time.Time `json:"next_billing_date"`
LastPaymentDate time.Time `json:"last_payment_date"`
LastPaymentAmount AmountPayout `json:"last_payment_amount"`
FinalPaymentDate time.Time `json:"final_payment_date"`
FailedPaymentCount int `json:"failed_payment_count,string"`
}
// Amount struct // Amount struct
Amount struct { Amount struct {
Currency string `json:"currency"` Currency string `json:"currency"`
@ -93,6 +108,26 @@ type (
SenderBatchHeader *SenderBatchHeader `json:"sender_batch_header,omitempty"` SenderBatchHeader *SenderBatchHeader `json:"sender_batch_header,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"`
}
// BillingPlan struct
BillingPlan struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Type string `json:"type,omitempty"`
PaymentDefinitions []PaymentDefinition `json:"payment_definitions,omitempty"`
MerchantPreferences *MerchantPreferences `json:"merchant_preferences,omitempty"`
}
// Capture struct // Capture struct
Capture struct { Capture struct {
Amount *Amount `json:"amount,omitempty"` Amount *Amount `json:"amount,omitempty"`
@ -105,14 +140,20 @@ type (
Links []Link `json:"links,omitempty"` Links []Link `json:"links,omitempty"`
} }
// ChargeModel struct
ChargeModel struct {
Type string `json:"type,omitempty"`
Amount AmountPayout `json:"amount,omitempty"`
}
// Client represents a Paypal REST API Client // Client represents a Paypal REST API Client
Client struct { Client struct {
Client *http.Client Client *http.Client
ClientID string ClientID string
Secret string Secret string
APIBase string APIBase string
Log io.Writer // If user set log file name all requests will be logged there Log io.Writer // If user set log file name all requests will be logged there
Token *TokenResponse Token *TokenResponse
tokenExpiresAt time.Time tokenExpiresAt time.Time
} }
@ -179,6 +220,19 @@ type (
Details string `json:"details"` Details string `json:"details"`
} }
// ExecuteAgreementResponse struct
ExecuteAgreementResponse struct {
ID string `json:"id"`
State string `json:"state"`
Description string `json:"description,omitempty"`
Payer Payer `json:"payer"`
Plan BillingPlan `json:"plan"`
StartDate time.Time `json:"start_date"`
ShippingAddress ShippingAddress `json:"shipping_address"`
AgreementDetails AgreementDetails `json:"agreement_details"`
Links []Link `json:"links"`
}
// ExecuteResponse struct // ExecuteResponse struct
ExecuteResponse struct { ExecuteResponse struct {
ID string `json:"id"` ID string `json:"id"`
@ -218,6 +272,16 @@ type (
Enctype string `json:"enctype,omitempty"` Enctype string `json:"enctype,omitempty"`
} }
// MerchantPreferences struct
MerchantPreferences struct {
SetupFee *AmountPayout `json:"setup_fee,omitempty"`
ReturnUrl string `json:"return_url,omitempty"`
CancelUrl string `json:"cancel_url,omitempty"`
AutoBillAmount string `json:"auto_bill_amount,omitempty"`
InitialFailAmountAction string `json:"initial_fail_amount_action,omitempty"`
MaxFailAttempts string `json:"max_fail_attempts,omitempty"`
}
// Order struct // Order struct
Order struct { Order struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
@ -263,6 +327,18 @@ type (
ExperienceProfileID string `json:"experience_profile_id,omitempty"` ExperienceProfileID string `json:"experience_profile_id,omitempty"`
} }
// PaymentDefinition struct
PaymentDefinition struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Frequency string `json:"frequency,omitempty"`
FrequencyInterval string `json:"frequency_interval,omitempty"`
Amount AmountPayout `json:"amount,omitempty"`
Cycles string `json:"cycles,omitempty"`
ChargeModels []ChargeModel `json:"charge_models,omitempty"`
}
// PaymentResponse structure // PaymentResponse structure
PaymentResponse struct { PaymentResponse struct {
ID string `json:"id"` ID string `json:"id"`
@ -449,3 +525,8 @@ type (
func (r *ErrorResponse) Error() string { func (r *ErrorResponse) Error() string {
return fmt.Sprintf("%v %v: %d %s", r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message) return fmt.Sprintf("%v %v: %d %s", r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message)
} }
func (t JsonTime) MarshalJSON() ([]byte, error) {
stamp := fmt.Sprintf(`"%s"`, time.Time(t).UTC().Format(time.RFC3339))
return []byte(stamp), nil
}