Files

603 lines
25 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package stripe
import (
"context"
"encoding/json"
"fmt"
"math/big"
"strings"
"time"
"github.com/hanzo-fi/go-libs/v5/pkg/types/currency"
"github.com/hanzo-fi/payments/ce/plugins/stripe/client"
"github.com/hanzo-fi/payments/pkg/domain/models"
"github.com/hanzo-fi/payments/pkg/domain/plugins"
"github.com/pkg/errors"
stripesdk "github.com/stripe/stripe-go/v80"
)
var (
ErrUnsupportedTransactionType = errors.New("unsupported TransactionType")
ErrUnsupportedAdjustment = errors.New("unsupported adjustment")
// ErrUnsupportedCurrency wraps plugins.ErrCurrencyNotSupported so the fetch
// loop can detect it via errors.Is and skip the transaction instead of
// failing — a returned error here would freeze ingestion on endless retries.
ErrUnsupportedCurrency = fmt.Errorf("unsupported currency: %w", plugins.ErrCurrencyNotSupported)
)
type paymentsState struct {
Timeline client.Timeline `json:"timeline"`
}
func (p *Plugin) fetchNextPayments(ctx context.Context, req models.FetchNextPaymentsRequest) (models.FetchNextPaymentsResponse, error) {
var oldState paymentsState
if req.State != nil {
if err := json.Unmarshal(req.State, &oldState); err != nil {
return models.FetchNextPaymentsResponse{}, err
}
}
var from models.PSPAccount
if req.FromPayload == nil {
return models.FetchNextPaymentsResponse{}, errors.New("missing from payload when fetching payments")
}
if err := json.Unmarshal(req.FromPayload, &from); err != nil {
return models.FetchNextPaymentsResponse{}, err
}
var payments []models.PSPPayment
newState := oldState
rawPayments, timeline, hasMore, err := p.client.GetPayments(
ctx,
resolveAccount(from.Reference),
oldState.Timeline,
int64(req.PageSize),
)
if err != nil {
return models.FetchNextPaymentsResponse{}, err
}
newState.Timeline = timeline
for _, rawPayment := range rawPayments {
payment, err := p.translatePayment(&from.Reference, rawPayment)
if err != nil {
if errors.Is(err, plugins.ErrCurrencyNotSupported) {
// Skip unsupported currencies rather than failing: a retryable
// error here would freeze ingestion for the whole account.
p.logger.WithField("reference", rawPayment.ID).Info("skipping payment with unsupported currency")
continue
}
return models.FetchNextPaymentsResponse{}, fmt.Errorf("failed to translate payment: %w", err)
}
// skip unsupported payments
if payment == nil {
continue
}
payments = append(payments, *payment)
}
payload, err := json.Marshal(newState)
if err != nil {
return models.FetchNextPaymentsResponse{}, err
}
return models.FetchNextPaymentsResponse{
Payments: payments,
NewState: payload,
HasMore: hasMore,
}, nil
}
func (p *Plugin) translatePayment(accountRef *string, balanceTransaction *stripesdk.BalanceTransaction) (payment *models.PSPPayment, err error) {
if balanceTransaction.Source == nil {
p.logger.WithFields(map[string]any{
"type": balanceTransaction.Type,
"id": balanceTransaction.ID,
}).Info("skipping balance transaction with nil source element")
return nil, nil
}
rawData, err := json.Marshal(balanceTransaction)
if err != nil {
return nil, fmt.Errorf("failed to marshal raw data: %w", err)
}
metadata := make(map[string]string)
switch balanceTransaction.Type {
case stripesdk.BalanceTransactionTypeCharge:
if balanceTransaction.Source.Charge == nil {
p.logger.WithFields(map[string]any{
"type": balanceTransaction.Type,
"id": balanceTransaction.ID,
}).Info("skipping balance transaction with nil charge element")
return nil, nil
}
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Charge.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
appendMetadata(metadata, balanceTransaction.Source.Charge.Metadata)
if balanceTransaction.Source.Charge.PaymentIntent != nil {
appendMetadata(metadata, balanceTransaction.Source.Charge.PaymentIntent.Metadata)
}
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
Type: models.PAYMENT_TYPE_PAYIN,
Status: models.PAYMENT_STATUS_SUCCEEDED,
Amount: big.NewInt(balanceTransaction.Source.Charge.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: detailsToPaymentScheme(balanceTransaction.Source.Charge.PaymentMethodDetails),
CreatedAt: time.Unix(balanceTransaction.Created, 0),
DestinationAccountReference: accountRef,
Metadata: metadata,
Raw: rawData,
}
case stripesdk.BalanceTransactionTypeRefund:
// Refund a charge
// Created when a credit card charge refund is initiated.
// If you authorize and capture separately and the capture amount is
// less than the initial authorization, you see a balance transaction
// of type charge for the full authorization amount and another balance
// transaction of type refund for the uncaptured portion.
// cf https://stripe.com/docs/reports/balance-transaction-types
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Refund.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
if balanceTransaction.Source.Refund.Charge == nil {
return nil, fmt.Errorf("refund charge missing from refund payload: %q", balanceTransaction.ID)
}
appendMetadata(metadata, balanceTransaction.Source.Refund.Metadata)
if balanceTransaction.Source.Refund.PaymentIntent != nil {
appendMetadata(metadata, balanceTransaction.Source.Refund.PaymentIntent.Metadata)
}
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: balanceTransaction.Source.Refund.Charge.BalanceTransaction.ID,
Type: models.PAYMENT_TYPE_PAYIN,
Status: models.PAYMENT_STATUS_REFUNDED,
Amount: big.NewInt(balanceTransaction.Source.Refund.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: detailsToPaymentScheme(balanceTransaction.Source.Refund.Charge.PaymentMethodDetails),
CreatedAt: time.Unix(balanceTransaction.Created, 0),
DestinationAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
case stripesdk.BalanceTransactionTypeRefundFailure:
// Refund a charge
// Created when a credit card charge refund fails, and Stripe returns the funds to your balance.
// This may occur if your customer’s bank or card issuer is unable to correctly process a refund
// (e.g., due to a closed bank account or a problem with the card).
// cf https://stripe.com/docs/reports/balance-transaction-types
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Refund.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
if balanceTransaction.Source.Refund.Charge == nil {
return nil, fmt.Errorf("refund charge missing from refund payload: %q", balanceTransaction.ID)
}
appendMetadata(metadata, balanceTransaction.Source.Refund.Metadata)
if balanceTransaction.Source.Refund.PaymentIntent != nil {
appendMetadata(metadata, balanceTransaction.Source.Refund.PaymentIntent.Metadata)
}
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: balanceTransaction.Source.Refund.Charge.BalanceTransaction.ID,
Type: models.PAYMENT_TYPE_PAYIN,
Status: models.PAYMENT_STATUS_REFUNDED_FAILURE,
Amount: big.NewInt(balanceTransaction.Source.Refund.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: detailsToPaymentScheme(balanceTransaction.Source.Refund.Charge.PaymentMethodDetails),
CreatedAt: time.Unix(balanceTransaction.Created, 0),
DestinationAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
case stripesdk.BalanceTransactionTypePayment:
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Charge.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
appendMetadata(metadata, balanceTransaction.Source.Charge.Metadata)
if balanceTransaction.Source.Charge.PaymentIntent != nil {
appendMetadata(metadata, balanceTransaction.Source.Charge.PaymentIntent.Metadata)
}
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
Type: models.PAYMENT_TYPE_PAYIN,
Status: models.PAYMENT_STATUS_SUCCEEDED,
Amount: big.NewInt(balanceTransaction.Source.Charge.Amount - balanceTransaction.Source.Charge.AmountRefunded),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: models.PAYMENT_SCHEME_OTHER,
CreatedAt: time.Unix(balanceTransaction.Created, 0),
DestinationAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
case stripesdk.BalanceTransactionTypePaymentRefund:
// Refund a payment
// Created when a local payment method refund is initiated.
// Additionally, if your customer’s bank or card issuer is unable to correctly process a refund
// (e.g., due to a closed bank account or a problem with the card) Stripe returns the funds to your balance.
// The returned funds are represented as a Balance transaction with the type payment_refund.
// cf https://stripe.com/docs/reports/balance-transaction-types
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Refund.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
if balanceTransaction.Source.Refund.Charge == nil {
return nil, fmt.Errorf("refund charge missing from payment refund payload: %q", balanceTransaction.ID)
}
appendMetadata(metadata, balanceTransaction.Source.Refund.Charge.Metadata)
if balanceTransaction.Source.Refund.Charge.PaymentIntent != nil {
appendMetadata(metadata, balanceTransaction.Source.Refund.Charge.PaymentIntent.Metadata)
}
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: balanceTransaction.Source.Refund.Charge.BalanceTransaction.ID,
Type: models.PAYMENT_TYPE_PAYIN,
Status: models.PAYMENT_STATUS_REFUNDED,
Amount: big.NewInt(balanceTransaction.Source.Refund.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: models.PAYMENT_SCHEME_OTHER,
CreatedAt: time.Unix(balanceTransaction.Created, 0),
DestinationAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
case stripesdk.BalanceTransactionTypePaymentFailureRefund:
// Refund a payment
// ACH, direct debit, and other delayed notification payment methods remain in a pending state
// until they either succeed or fail. You’ll see a pending Balance transaction of type payment
// when the payment is created. Another Balance transaction of type payment_failure_refund appears
// if the pending payment later fails.
// cf https://stripe.com/docs/reports/balance-transaction-types
var transactionCurrency string
var amount int64
var parentReference string
if balanceTransaction.Source.Refund == nil {
p.logger.WithFields(map[string]any{"type": balanceTransaction.Type, "reference": balanceTransaction.ID}).Infof("Handling a payment_failure_refund balance transaction without a refund source, we will store it as a single payment as we cannot trace to the original payment")
transactionCurrency = strings.ToUpper(string(balanceTransaction.Currency))
amount = balanceTransaction.Amount
parentReference = ""
} else {
if balanceTransaction.Source.Refund.Charge == nil {
return nil, fmt.Errorf("refund charge missing from payment refund failure payload: %q", balanceTransaction.ID)
}
transactionCurrency = strings.ToUpper(string(balanceTransaction.Source.Refund.Currency))
amount = balanceTransaction.Source.Refund.Amount
parentReference = balanceTransaction.Source.Refund.Charge.BalanceTransaction.ID
appendMetadata(metadata, balanceTransaction.Source.Refund.Charge.Metadata)
if balanceTransaction.Source.Refund.Charge.PaymentIntent != nil {
appendMetadata(metadata, balanceTransaction.Source.Refund.Charge.PaymentIntent.Metadata)
}
}
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: parentReference,
Type: models.PAYMENT_TYPE_PAYIN,
Status: models.PAYMENT_STATUS_REFUNDED_FAILURE,
Amount: big.NewInt(amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: models.PAYMENT_SCHEME_OTHER,
CreatedAt: time.Unix(balanceTransaction.Created, 0),
DestinationAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
case stripesdk.BalanceTransactionTypePayout:
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Payout.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
appendMetadata(metadata, balanceTransaction.Source.Payout.Metadata)
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
Type: models.PAYMENT_TYPE_PAYOUT,
Status: convertPayoutStatus(balanceTransaction.Source.Payout.Status),
Amount: big.NewInt(balanceTransaction.Source.Payout.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: func() models.PaymentScheme {
switch balanceTransaction.Source.Payout.Type {
case stripesdk.PayoutTypeBank:
return models.PAYMENT_SCHEME_SEPA_CREDIT
case stripesdk.PayoutTypeCard:
return destinationToPaymentScheme(balanceTransaction.Source.Payout.Destination)
}
return models.PAYMENT_SCHEME_UNKNOWN
}(),
CreatedAt: time.Unix(balanceTransaction.Created, 0),
SourceAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
case stripesdk.BalanceTransactionTypePayoutFailure, stripesdk.BalanceTransactionTypePayoutCancel:
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Payout.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
status := models.PAYMENT_STATUS_FAILED
if balanceTransaction.Type == stripesdk.BalanceTransactionTypePayoutCancel {
status = models.PAYMENT_STATUS_CANCELLED
}
appendMetadata(metadata, balanceTransaction.Source.Payout.Metadata)
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: balanceTransaction.Source.Payout.BalanceTransaction.ID,
Type: models.PAYMENT_TYPE_PAYOUT,
Status: status,
Amount: big.NewInt(balanceTransaction.Source.Payout.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: func() models.PaymentScheme {
switch balanceTransaction.Source.Payout.Type {
case stripesdk.PayoutTypeBank:
return models.PAYMENT_SCHEME_SEPA_CREDIT
case stripesdk.PayoutTypeCard:
return destinationToPaymentScheme(balanceTransaction.Source.Payout.Destination)
}
return models.PAYMENT_SCHEME_UNKNOWN
}(),
CreatedAt: time.Unix(balanceTransaction.Created, 0),
SourceAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
case stripesdk.BalanceTransactionTypeTransfer:
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Transfer.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
appendMetadata(metadata, balanceTransaction.Source.Transfer.Metadata)
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
Type: models.PAYMENT_TYPE_TRANSFER,
Status: models.PAYMENT_STATUS_SUCCEEDED,
Amount: big.NewInt(balanceTransaction.Source.Transfer.Amount - balanceTransaction.Source.Transfer.AmountReversed),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: models.PAYMENT_SCHEME_OTHER,
CreatedAt: time.Unix(balanceTransaction.Created, 0),
SourceAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
if balanceTransaction.Source.Transfer.Destination != nil {
payment.DestinationAccountReference = &balanceTransaction.Source.Transfer.Destination.ID
}
case stripesdk.BalanceTransactionTypeTransferRefund:
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Transfer.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
appendMetadata(metadata, balanceTransaction.Source.Transfer.Metadata)
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: balanceTransaction.Source.Transfer.BalanceTransaction.ID,
Type: models.PAYMENT_TYPE_TRANSFER,
Status: models.PAYMENT_STATUS_REFUNDED,
Amount: big.NewInt(balanceTransaction.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: models.PAYMENT_SCHEME_OTHER,
SourceAccountReference: accountRef,
CreatedAt: time.Unix(balanceTransaction.Created, 0),
Raw: rawData,
Metadata: metadata,
}
if balanceTransaction.Source.Transfer.Destination != nil {
payment.DestinationAccountReference = &balanceTransaction.Source.Transfer.Destination.ID
}
case stripesdk.BalanceTransactionTypeTransferCancel, stripesdk.BalanceTransactionTypeTransferFailure:
transactionCurrency := strings.ToUpper(string(balanceTransaction.Source.Transfer.Currency))
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
status := models.PAYMENT_STATUS_FAILED
if balanceTransaction.Type == stripesdk.BalanceTransactionTypeTransferCancel {
status = models.PAYMENT_STATUS_CANCELLED
}
appendMetadata(metadata, balanceTransaction.Source.Transfer.Metadata)
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: balanceTransaction.Source.Transfer.BalanceTransaction.ID,
Type: models.PAYMENT_TYPE_TRANSFER,
Status: status,
Amount: big.NewInt(balanceTransaction.Amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: models.PAYMENT_SCHEME_OTHER,
SourceAccountReference: accountRef,
CreatedAt: time.Unix(balanceTransaction.Created, 0),
Raw: rawData,
Metadata: metadata,
}
if balanceTransaction.Source.Transfer.Destination != nil {
payment.DestinationAccountReference = &balanceTransaction.Source.Transfer.Destination.ID
}
case stripesdk.BalanceTransactionTypeAdjustment:
var transactionCurrency string
var amount int64
var parentReference string
var scheme models.PaymentScheme
var paymentStatus models.PaymentStatus
if balanceTransaction.Source.Dispute == nil {
p.logger.WithField("type", balanceTransaction.Type).WithField("reference", balanceTransaction.ID).Infof("skipping unsupported balance transaction: %v", ErrUnsupportedAdjustment)
return nil, nil
}
if balanceTransaction.Source.Dispute.Charge == nil {
return nil, fmt.Errorf("refund charge missing from payment dispute payload: %q", balanceTransaction.ID)
}
transactionCurrency = strings.ToUpper(string(balanceTransaction.Source.Dispute.Charge.Currency))
amount = balanceTransaction.Source.Dispute.Charge.Amount
parentReference = balanceTransaction.Source.Dispute.Charge.BalanceTransaction.ID
scheme = detailsToPaymentScheme(balanceTransaction.Source.Dispute.Charge.PaymentMethodDetails)
disputeStatus := convertDisputeStatus(balanceTransaction.Source.Dispute.Status)
paymentStatus = models.PAYMENT_STATUS_DISPUTE
switch disputeStatus {
case models.PAYMENT_STATUS_DISPUTE_WON:
paymentStatus = models.PAYMENT_STATUS_SUCCEEDED
case models.PAYMENT_STATUS_DISPUTE_LOST:
paymentStatus = models.PAYMENT_STATUS_FAILED
}
appendMetadata(metadata, balanceTransaction.Source.Dispute.Charge.Metadata)
if balanceTransaction.Source.Dispute.Charge.PaymentIntent != nil {
appendMetadata(metadata, balanceTransaction.Source.Dispute.Charge.PaymentIntent.Metadata)
}
_, ok := supportedCurrenciesWithDecimal[transactionCurrency]
if !ok {
return nil, fmt.Errorf("%w %q", ErrUnsupportedCurrency, transactionCurrency)
}
payment = &models.PSPPayment{
Reference: balanceTransaction.ID,
ParentReference: parentReference,
Type: models.PAYMENT_TYPE_PAYIN,
Status: paymentStatus, // Dispute is occurring, we don't know the outcome yet
Amount: big.NewInt(amount),
Asset: currency.FormatAsset(supportedCurrenciesWithDecimal, transactionCurrency),
Scheme: scheme,
CreatedAt: time.Unix(balanceTransaction.Created, 0),
DestinationAccountReference: accountRef,
Raw: rawData,
Metadata: metadata,
}
default:
p.logger.WithField("type", balanceTransaction.Type).WithField("reference", balanceTransaction.ID).Errorf("unsupported balance transaction type: %v", ErrUnsupportedTransactionType)
return nil, nil
}
return payment, err
}
func convertDisputeStatus(status stripesdk.DisputeStatus) models.PaymentStatus {
switch status {
case stripesdk.DisputeStatusNeedsResponse, stripesdk.DisputeStatusUnderReview:
return models.PAYMENT_STATUS_DISPUTE
case stripesdk.DisputeStatusLost:
return models.PAYMENT_STATUS_DISPUTE_LOST
case stripesdk.DisputeStatusWon:
return models.PAYMENT_STATUS_DISPUTE_WON
default:
return models.PAYMENT_STATUS_DISPUTE
}
}
func convertPayoutStatus(status stripesdk.PayoutStatus) models.PaymentStatus {
switch status {
case stripesdk.PayoutStatusCanceled:
return models.PAYMENT_STATUS_CANCELLED
case stripesdk.PayoutStatusFailed:
return models.PAYMENT_STATUS_FAILED
case stripesdk.PayoutStatusInTransit, stripesdk.PayoutStatusPending:
return models.PAYMENT_STATUS_PENDING
case stripesdk.PayoutStatusPaid:
return models.PAYMENT_STATUS_SUCCEEDED
}
return models.PAYMENT_STATUS_OTHER
}
func appendMetadata(s map[string]string, vs ...map[string]string) {
for _, in := range vs {
if in == nil {
continue
}
for k, v := range in {
s[k] = v
}
}
}
func detailsToPaymentScheme(details *stripesdk.ChargePaymentMethodDetails) models.PaymentScheme {
if details == nil || details.Card == nil {
return models.PAYMENT_SCHEME_UNKNOWN
}
scheme, _ := models.PaymentSchemeFromString(
fmt.Sprintf("CARD_%s", strings.ToUpper(string(details.Card.Brand))),
)
return scheme
}
func destinationToPaymentScheme(dest *stripesdk.PayoutDestination) models.PaymentScheme {
if dest == nil || dest.Card == nil {
return models.PAYMENT_SCHEME_UNKNOWN
}
switch dest.Card.Brand {
case stripesdk.CardBrandAmericanExpress:
return models.PAYMENT_SCHEME_CARD_AMEX
case stripesdk.CardBrandDiscover:
return models.PAYMENT_SCHEME_CARD_DISCOVER
case stripesdk.CardBrandDinersClub:
return models.PAYMENT_SCHEME_CARD_DINERS
case stripesdk.CardBrandJCB:
return models.PAYMENT_SCHEME_CARD_JCB
case stripesdk.CardBrandMasterCard:
return models.PAYMENT_SCHEME_CARD_MASTERCARD
case stripesdk.CardBrandUnionPay:
return models.PAYMENT_SCHEME_CARD_UNION_PAY
case stripesdk.CardBrandVisa:
return models.PAYMENT_SCHEME_CARD_VISA
default:
return models.PAYMENT_SCHEME_UNKNOWN
}
}