From 433a6e7ff46ad36ddbdb58c707c2b192de46501c Mon Sep 17 00:00:00 2001 From: Remi Jannel Date: Mon, 4 Nov 2019 20:37:41 -0800 Subject: [PATCH 1/5] Add support for `Mandate` --- client/api.go | 4 ++ mandate.go | 119 +++++++++++++++++++++++++++++++++++++++++ mandate/client.go | 31 +++++++++++ mandate/client_test.go | 14 +++++ 4 files changed, 168 insertions(+) create mode 100644 mandate.go create mode 100644 mandate/client.go create mode 100644 mandate/client_test.go diff --git a/client/api.go b/client/api.go index 77f664fa10..ccb6ba48af 100644 --- a/client/api.go +++ b/client/api.go @@ -37,6 +37,7 @@ import ( issuingdispute "github.com/stripe/stripe-go/issuing/dispute" "github.com/stripe/stripe-go/issuing/transaction" "github.com/stripe/stripe-go/loginlink" + "github.com/stripe/stripe-go/mandate" "github.com/stripe/stripe-go/oauth" "github.com/stripe/stripe-go/order" "github.com/stripe/stripe-go/orderreturn" @@ -148,6 +149,8 @@ type API struct { IssuingTransactions *transaction.Client // LoginLinks is the client used to invoke login link related APIs. LoginLinks *loginlink.Client + // Mandates is the client used to invoke mandates related APIs. + Mandates *mandate.Client // OAuth is the client used to invoke /oauth APIs. OAuth *oauth.Client // Orders is the client used to invoke /orders APIs. @@ -273,6 +276,7 @@ func (a *API) Init(key string, backends *stripe.Backends) { a.IssuingDisputes = &issuingdispute.Client{B: backends.API, Key: key} a.IssuingTransactions = &transaction.Client{B: backends.API, Key: key} a.LoginLinks = &loginlink.Client{B: backends.API, Key: key} + a.Mandates = &mandate.Client{B: backends.API, Key: key} a.OAuth = &oauth.Client{B: backends.Connect, Key: key} a.OrderReturns = &orderreturn.Client{B: backends.API, Key: key} a.Orders = &order.Client{B: backends.API, Key: key} diff --git a/mandate.go b/mandate.go new file mode 100644 index 0000000000..d14ae4d768 --- /dev/null +++ b/mandate.go @@ -0,0 +1,119 @@ +package stripe + +import "encoding/json" + +// List of values that MandateStatus can take. +const ( + MandateCustomerAcceptanceTypeOffline MandateCustomerAcceptanceType = "offline" + MandateCustomerAcceptanceTypeOnline MandateCustomerAcceptanceType = "online" +) + +// MandateCustomerAcceptanceType is the list of allowed values for the type of customer acceptance +// for a given mandate.. +type MandateCustomerAcceptanceType string + +// List of values that MandateStatus can take. +const ( + MandateStatusActive MandateStatus = "active" + MandateStatusInactive MandateStatus = "inactive" + MandateStatusPending MandateStatus = "pending" +) + +// MandateStatus is the list of allowed values for the mandate status. +type MandateStatus string + +// List of values that MandateType can take. +const ( + MandateTypeMultiUse MandateType = "multi_use" + MandateTypeSingleUse MandateType = "single_use" +) + +// MandateType is the list of allowed values for the mandate type. +type MandateType string + +// MandateParams is the set of parameters that can be used when retrieving a mandate. +type MandateParams struct { + Params `form:"*"` +} + +// MandateCustomerAcceptanceOffline represents details about the customer acceptance of an offline +// mandate. +type MandateCustomerAcceptanceOffline struct { +} + +// MandateCustomerAcceptanceOnline represents details about the customer acceptance of an online +// mandate. +type MandateCustomerAcceptanceOnline struct { + IPAddress string `json:"ip_address"` + UserAgent string `json:"user_agent"` +} + +// MandateCustomerAcceptance represents details about the customer acceptance for a mandate. +type MandateCustomerAcceptance struct { + AcceptedAt int64 `json:"accepted_at"` + Offline *MandateCustomerAcceptanceOffline `json:"offline"` + Online *MandateCustomerAcceptanceOnline `json:"online"` + Type MandateCustomerAcceptanceType `json:"type"` +} + +// MandateMultiUse represents details about a multi-use mandate. +type MandateMultiUse struct { +} + +// MandatePaymentMethodDetailsCard represents details about the card associated with this mandate. +type MandatePaymentMethodDetailsCard struct { +} + +// MandatePaymentMethodDetailsSepaDebit represents details about the SEPA debit bank account +// associated with this mandate. +type MandatePaymentMethodDetailsSepaDebit struct { + Reference string `json:"reference"` + URL string `json:"url"` +} + +// MandatePaymentMethodDetails represents details about the payment method associated with this +// mandate. +type MandatePaymentMethodDetails struct { + Card *MandatePaymentMethodDetailsCard `json:"card"` + SepaDebit *MandatePaymentMethodDetailsSepaDebit `json:"sepa_debit"` + Type PaymentMethodType `json:"type"` +} + +// MandateSingleUse represents details about a single-use mandate. +type MandateSingleUse struct { + Amount int64 `json:"amount"` + Currency Currency `json:"currency"` +} + +// Mandate is the resource representing a Mandate. +type Mandate struct { + CustomerAcceptance *MandateCustomerAcceptance `json:"customer_acceptance"` + ID string `json:"id"` + Livemode bool `json:"livemode"` + MultiUse *MandateMultiUse `json:"multi_use"` + Object string `json:"object"` + PaymentMethod *PaymentMethod `json:"payment_method"` + PaymentMethodDetails *MandatePaymentMethodDetails `json:"payment_method_details"` + SingleUse *MandateSingleUse `json:"single_use"` + Status MandateStatus `json:"status"` + Type MandateType `json:"type"` +} + +// UnmarshalJSON handles deserialization of a Mandate. +// This custom unmarshaling is needed because the resulting +// property may be an id or the full struct if it was expanded. +func (i *Mandate) UnmarshalJSON(data []byte) error { + if id, ok := ParseID(data); ok { + i.ID = id + return nil + } + + type ma Mandate + var v ma + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + *i = Mandate(v) + return nil +} diff --git a/mandate/client.go b/mandate/client.go new file mode 100644 index 0000000000..dab04c5585 --- /dev/null +++ b/mandate/client.go @@ -0,0 +1,31 @@ +// Package mandate provides the /mandates APIs +package mandate + +import ( + "net/http" + + stripe "github.com/stripe/stripe-go" +) + +// Client is used to invoke mandates APIs. +type Client struct { + B stripe.Backend + Key string +} + +// Get returns the details of a Mandate. +func Get(id string, params *stripe.MandateParams) (*stripe.Mandate, error) { + return getC().Get(id, params) +} + +// Get returns the details of a Mandate. +func (c Client) Get(id string, params *stripe.MandateParams) (*stripe.Mandate, error) { + path := stripe.FormatURLPath("/v1/mandates/%s", id) + mandate := &stripe.Mandate{} + err := c.B.Call(http.MethodGet, path, c.Key, params, mandate) + return mandate, err +} + +func getC() Client { + return Client{stripe.GetBackend(stripe.APIBackend), stripe.Key} +} diff --git a/mandate/client_test.go b/mandate/client_test.go new file mode 100644 index 0000000000..ab4574c138 --- /dev/null +++ b/mandate/client_test.go @@ -0,0 +1,14 @@ +package mandate + +import ( + "testing" + + assert "github.com/stretchr/testify/require" + _ "github.com/stripe/stripe-go/testing" +) + +func TestMandateMethodGet(t *testing.T) { + pm, err := Get("mandate_123", nil) + assert.Nil(t, err) + assert.NotNil(t, pm) +} From dd835bd942052cef1781a8036e77439b16000333 Mon Sep 17 00:00:00 2001 From: Remi Jannel Date: Mon, 4 Nov 2019 20:52:30 -0800 Subject: [PATCH 2/5] Add support for iDEAL and SEPA Debit on `PaymentMethod` --- paymentmethod.go | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/paymentmethod.go b/paymentmethod.go index 2adf1183eb..6aa07a0735 100644 --- a/paymentmethod.go +++ b/paymentmethod.go @@ -18,6 +18,8 @@ type PaymentMethodType string const ( PaymentMethodTypeCard PaymentMethodType = "card" PaymentMethodTypeCardPresent PaymentMethodType = "card_present" + PaymentMethodTypeIdeal PaymentMethodType = "ideal" + PaymentMethodTypeSepaDebit PaymentMethodType = "sepa_debit" ) // PaymentMethodCardBrand is the list of allowed values for the brand property on a @@ -76,6 +78,18 @@ type PaymentMethodFPXParams struct { Bank *string `form:"bank"` } +// PaymentMethodIdealParams is the set of parameters allowed for the `ideal` hash when creating a +// PaymentMethod of type ideal. +type PaymentMethodIdealParams struct { + Bank *string `form:"bank"` +} + +// PaymentMethodSepaDebitParams is the set of parameters allowed for the `sepa_debit` hash when +// creating a PaymentMethod of type sepa_debit. +type PaymentMethodSepaDebitParams struct { + Iban *string `form:"iban"` +} + // PaymentMethodParams is the set of parameters that can be used when creating or updating a // PaymentMethod. type PaymentMethodParams struct { @@ -137,7 +151,7 @@ type PaymentMethodCardWallet struct { Type PaymentMethodCardWalletType `json:"type"` } -// PaymentMethodCard represents the card-specific properties on a PaymentMethod. +// PaymentMethodCard represents the card-specific properties. type PaymentMethodCard struct { Brand PaymentMethodCardBrand `json:"brand"` Checks *PaymentMethodCardChecks `json:"checks"` @@ -157,17 +171,32 @@ type PaymentMethodCard struct { Issuer string `json:"issuer"` } -// PaymentMethodCardPresent represents the card-present-specific properties on a PaymentMethod. +// PaymentMethodCardPresent represents the card-present-specific properties. type PaymentMethodCardPresent struct { } -// PaymentMethodFPX represents Malaysia FPX PaymentMethod (Malaysia Only). +// PaymentMethodFPX represents FPX-specific properties (Malaysia Only). type PaymentMethodFPX struct { AccountHolderType PaymentMethodFPXAccountHolderType `json:"account_holder_type"` Bank string `json:"bank"` TransactionID string `json:"transaction_id"` } +// PaymentMethodIdeal represents the iDEAL-specific properties. +type PaymentMethodIdeal struct { + Bank string `json:"bank"` + Bic string `json:"bic"` +} + +// PaymentMethodSepaDebit represents the SEPA-debit-specific properties. +type PaymentMethodSepaDebit struct { + BankCode string `json:"bank_code"` + BranchCode string `json:"branch_code"` + Country string `json:"country"` + Fingerprint string `json:"fingerprint"` + Last4 string `json:"last4"` +} + // PaymentMethod is the resource representing a PaymentMethod. type PaymentMethod struct { BillingDetails *BillingDetails `json:"billing_details"` @@ -177,9 +206,11 @@ type PaymentMethod struct { Customer *Customer `json:"customer"` FPX *PaymentMethodFPX `json:"fpx"` ID string `json:"id"` + Ideal *PaymentMethodIdeal `json:"ideal"` Livemode bool `json:"livemode"` Metadata map[string]string `json:"metadata"` Object string `json:"object"` + SepaDebit *PaymentMethodSepaDebit `json:"sepa_debit"` Type PaymentMethodType `json:"type"` } From 3177b990d7141349441d04aed0745601b13170a3 Mon Sep 17 00:00:00 2001 From: Remi Jannel Date: Mon, 4 Nov 2019 21:08:16 -0800 Subject: [PATCH 3/5] Add support for `Mandate` on `SetupIntent` --- setupintent.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/setupintent.go b/setupintent.go index ff67574b01..9d92d56295 100644 --- a/setupintent.go +++ b/setupintent.go @@ -67,11 +67,39 @@ type SetupIntentCancelParams struct { // SetupIntentConfirmParams is the set of parameters that can be used when confirming a setup intent. type SetupIntentConfirmParams struct { Params `form:"*"` + MandateData *SetupIntentMandateDataParams `form:"mandate_data"` PaymentMethod *string `form:"payment_method"` PaymentMethodOptions *SetupIntentPaymentMethodOptionsParams `form:"payment_method_options"` ReturnURL *string `form:"return_url"` } +// SetupIntentMandateDataCustomerAcceptanceOfflineParams is the set of parameters for the customer +// acceptance of an offline mandate. +type SetupIntentMandateDataCustomerAcceptanceOfflineParams struct { +} + +// SetupIntentMandateDataCustomerAcceptanceOnlineParams is the set of parameters for the customer +// acceptance of an online mandate. +type SetupIntentMandateDataCustomerAcceptanceOnlineParams struct { + IPAddress *string `form:"ip_address"` + UserAgent *string `form:"user_agent"` +} + +// SetupIntentMandateDataCustomerAcceptanceParams is the set of parameters for the customer +// acceptance of a mandate. +type SetupIntentMandateDataCustomerAcceptanceParams struct { + AcceptedAt int64 `form:"accepted_at"` + Offline *SetupIntentMandateDataCustomerAcceptanceOfflineParams `form:"offline"` + Online *SetupIntentMandateDataCustomerAcceptanceOnlineParams `form:"online"` + Type MandateCustomerAcceptanceType `form:"type"` +} + +// SetupIntentMandateDataParams is the set of parameters controlling the creation of the mandate +// associated with this SetupIntent. +type SetupIntentMandateDataParams struct { + CustomerAcceptance *SetupIntentMandateDataCustomerAcceptanceParams `form:"customer_acceptance"` +} + // SetupIntentPaymentMethodOptionsCardParams represents the card-specific options applied to a // SetupIntent. type SetupIntentPaymentMethodOptionsCardParams struct { @@ -85,17 +113,25 @@ type SetupIntentPaymentMethodOptionsParams struct { Card *SetupIntentPaymentMethodOptionsCardParams `form:"card"` } +// SetupIntentSingleUseParams represents the single-use mandate-specific parameters. +type SetupIntentSingleUseParams struct { + Amount *int64 `form:"amount"` + Currency *string `form:"currency"` +} + // SetupIntentParams is the set of parameters that can be used when handling a setup intent. type SetupIntentParams struct { Params `form:"*"` Confirm *bool `form:"confirm"` Customer *string `form:"customer"` Description *string `form:"description"` + MandateData *SetupIntentMandateDataParams `form:"mandate_data"` OnBehalfOf *string `form:"on_behalf_of"` PaymentMethod *string `form:"payment_method"` PaymentMethodOptions *SetupIntentPaymentMethodOptionsParams `form:"payment_method_options"` PaymentMethodTypes []*string `form:"payment_method_types"` ReturnURL *string `form:"return_url"` + SingleUse *SetupIntentSingleUseParams `form:"single_use"` Usage *string `form:"usage"` } @@ -146,6 +182,7 @@ type SetupIntent struct { ID string `json:"id"` LastSetupError *Error `json:"last_setup_error"` Livemode bool `json:"livemode"` + Mandate *Mandate `json:"mandate"` Metadata map[string]string `json:"metadata"` NextAction *SetupIntentNextAction `json:"next_action"` Object string `json:"object"` @@ -153,6 +190,7 @@ type SetupIntent struct { PaymentMethod *PaymentMethod `json:"payment_method"` PaymentMethodOptions *SetupIntentPaymentMethodOptions `json:"payment_method_options"` PaymentMethodTypes []string `json:"payment_method_types"` + SingleUseMandate *Mandate `json:"single_use_mandate"` Status SetupIntentStatus `json:"status"` Usage SetupIntentUsage `json:"usage"` } From 417145b5274f5b483f9d52f140d3be587acf9bc4 Mon Sep 17 00:00:00 2001 From: Remi Jannel Date: Mon, 4 Nov 2019 21:11:24 -0800 Subject: [PATCH 4/5] Add support for `Mandate` on `PaymentIntent` --- paymentintent.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/paymentintent.go b/paymentintent.go index 22c1119cb8..12b3fff5b4 100644 --- a/paymentintent.go +++ b/paymentintent.go @@ -124,6 +124,8 @@ type PaymentIntentCaptureParams struct { // PaymentIntentConfirmParams is the set of parameters that can be used when confirming a payment intent. type PaymentIntentConfirmParams struct { Params `form:"*"` + Mandate *string `form:"mandate"` + MandateData *PaymentIntentMandateDataParams `form:"mandate_data"` OffSession *bool `form:"off_session"` PaymentMethod *string `form:"payment_method"` PaymentMethodOptions *PaymentIntentPaymentMethodOptionsParams `form:"payment_method_options"` @@ -137,6 +139,33 @@ type PaymentIntentConfirmParams struct { UseStripeSDK *bool `form:"use_stripe_sdk"` } +// PaymentIntentMandateDataCustomerAcceptanceOfflineParams is the set of parameters for the customer +// acceptance of an offline mandate. +type PaymentIntentMandateDataCustomerAcceptanceOfflineParams struct { +} + +// PaymentIntentMandateDataCustomerAcceptanceOnlineParams is the set of parameters for the customer +// acceptance of an online mandate. +type PaymentIntentMandateDataCustomerAcceptanceOnlineParams struct { + IPAddress *string `form:"ip_address"` + UserAgent *string `form:"user_agent"` +} + +// PaymentIntentMandateDataCustomerAcceptanceParams is the set of parameters for the customer +// acceptance of a mandate. +type PaymentIntentMandateDataCustomerAcceptanceParams struct { + AcceptedAt int64 `form:"accepted_at"` + Offline *PaymentIntentMandateDataCustomerAcceptanceOfflineParams `form:"offline"` + Online *PaymentIntentMandateDataCustomerAcceptanceOnlineParams `form:"online"` + Type MandateCustomerAcceptanceType `form:"type"` +} + +// PaymentIntentMandateDataParams is the set of parameters controlling the creation of the mandate +// associated with this PaymentIntent. +type PaymentIntentMandateDataParams struct { + CustomerAcceptance *PaymentIntentMandateDataCustomerAcceptanceParams `form:"customer_acceptance"` +} + // PaymentIntentPaymentMethodOptionsCardInstallmentsPlanParams represents details about the // installment plan chosen for this payment intent. type PaymentIntentPaymentMethodOptionsCardInstallmentsPlanParams struct { @@ -183,6 +212,8 @@ type PaymentIntentParams struct { Currency *string `form:"currency"` Customer *string `form:"customer"` Description *string `form:"description"` + Mandate *string `form:"mandate"` + MandateData *PaymentIntentMandateDataParams `form:"mandate_data"` OnBehalfOf *string `form:"on_behalf_of"` PaymentMethod *string `form:"payment_method"` PaymentMethodOptions *PaymentIntentPaymentMethodOptionsParams `form:"payment_method_options"` From a857528ffbfbc1217585d8df7ecbba4df26dbfdc Mon Sep 17 00:00:00 2001 From: Remi Jannel Date: Tue, 5 Nov 2019 19:55:42 -0800 Subject: [PATCH 5/5] update stripe-mock --- .travis.yml | 2 +- testing/testing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9fa783af0c..41a36cca33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ cache: env: global: # If changing this number, please also change it in `testing/testing.go`. - - STRIPE_MOCK_VERSION=0.71.0 + - STRIPE_MOCK_VERSION=0.72.0 go: - "1.9.x" diff --git a/testing/testing.go b/testing/testing.go index a2dc6e38dc..d48c9e1e86 100644 --- a/testing/testing.go +++ b/testing/testing.go @@ -25,7 +25,7 @@ const ( // added in a more recent version of stripe-mock, we can show people a // better error message instead of the test suite crashing with a bunch of // confusing 404 errors or the like. - MockMinimumVersion = "0.71.0" + MockMinimumVersion = "0.72.0" // TestMerchantID is a token that can be used to represent a merchant ID in // simple tests.