Skip to content

Commit

Permalink
Merge pull request #986 from stripe/remi-add-mandate
Browse files Browse the repository at this point in the history
Adding support for iDEAL and SEPA debit on PaymentMethod
  • Loading branch information
ob-stripe authored Nov 6, 2019
2 parents 53ebf5a + a857528 commit 1658738
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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}
Expand Down
119 changes: 119 additions & 0 deletions mandate.go
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 31 additions & 0 deletions mandate/client.go
Original file line number Diff line number Diff line change
@@ -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}
}
14 changes: 14 additions & 0 deletions mandate/client_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
31 changes: 31 additions & 0 deletions paymentintent.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -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 {
Expand Down Expand Up @@ -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"`
Expand Down
37 changes: 34 additions & 3 deletions paymentmethod.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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"`
Expand All @@ -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"`
Expand All @@ -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"`
}

Expand Down
Loading

0 comments on commit 1658738

Please sign in to comment.