Adapt CreditCard cloner to clone not 'cards of the platform account to payment_methods of the connected accounts' but instead 'cards or payment_methods of the platform account to payment_methods of the connected accounts'

This process mimicks the existing process of generating a token on the connected account from a card on the platform account. In the Payment Intents API we need to create a payment method in the connected account, a token is not enough
This commit is contained in:
luisramos0
2020-01-27 16:43:31 +00:00
committed by Luis Ramos
parent 1afd712ff4
commit 5ef1510fc7
3 changed files with 38 additions and 27 deletions

View File

@@ -71,11 +71,9 @@ module Spree
options[:currency] = gateway_options[:currency]
options[:stripe_account] = stripe_account_id
Stripe::CreditCardCloner.new.clone!(creditcard, stripe_account_id)
options[:customer] = creditcard.gateway_customer_profile_id
payment_method = creditcard.gateway_payment_profile_id
[money, payment_method, options]
connected_acct_customer_id, connected_acct_payment_method_id = Stripe::CreditCardCloner.new.clone(creditcard, stripe_account_id)
options[:customer] = connected_acct_customer_id
[money, connected_acct_payment_method_id, options]
end
def failed_activemerchant_billing_response(error_message)

View File

@@ -1,44 +1,49 @@
# frozen_string_literal: true
# Here we clone
# - a card (card_*) stored in a customer in a platform account
# - a card (card_*) or payment_method (pm_*) stored in a customer in a platform account
# into
# - a payment method (pm_*) in a new customer in a connected account
# - a payment method (pm_*) in a new customer in a connected account
#
# This process is used in the migration between using the Stripe Charges API (stripe_connect)
# and the Stripe Payment Intents API (stripe_sca)
# This is required when using the Stripe Payment Intents API:
# - the customer and payment methods are stored in the platform account
# so that they can be re-used across multiple sellers
# - when a card needs to be charged, we need to create it in the seller's stripe account
#
# This process can be deleted once all hubs are running on the new stripe_sca method and all cards in the system have been migrated to the new payment_methods
# Basically, when all DBs have no card_* values in credit_card.gateway_payment_profile_id
# We are doing this process every time the card is charged:
# - this means that, if the customer uses the same card on the same seller multiple times,
# the card will be created multiple times on the seller's account
# - to avoid this, we would have to store the IDs of every card on each seller's stripe account
# in our database (this way we only have to store the platform account ID)
module Stripe
class CreditCardCloner
def clone!(credit_card, stripe_account_id)
return unless credit_card.gateway_payment_profile_id.starts_with?('card_')
def clone(credit_card, connected_account_id)
new_payment_method = clone_payment_method(credit_card, connected_account_id)
new_payment_method = clone_payment_method(credit_card, stripe_account_id)
new_customer = Stripe::Customer.create({ email: credit_card.user.email },
stripe_account: stripe_account_id)
attach_payment_method_to_customer(new_payment_method.id, new_customer.id, stripe_account_id)
stripe_account: connected_account_id)
attach_payment_method_to_customer(new_payment_method.id,
new_customer.id,
connected_account_id)
credit_card.update_attributes gateway_customer_profile_id: new_customer.id,
gateway_payment_profile_id: new_payment_method.id
credit_card
[new_customer.id, new_payment_method.id]
end
private
def clone_payment_method(credit_card, stripe_account_id)
card_id = credit_card.gateway_payment_profile_id
def clone_payment_method(credit_card, connected_account_id)
platform_acct_payment_method_id = credit_card.gateway_payment_profile_id
customer_id = credit_card.gateway_customer_profile_id
Stripe::PaymentMethod.create({ customer: customer_id, payment_method: card_id },
stripe_account: stripe_account_id)
Stripe::PaymentMethod.create({ customer: customer_id,
payment_method: platform_acct_payment_method_id },
stripe_account: connected_account_id)
end
def attach_payment_method_to_customer(payment_method_id, customer_id, stripe_account_id)
def attach_payment_method_to_customer(payment_method_id, customer_id, connected_account_id)
Stripe::PaymentMethod.attach(payment_method_id,
{ customer: customer_id },
stripe_account: stripe_account_id)
stripe_account: connected_account_id)
end
end
end

View File

@@ -5,7 +5,7 @@ require 'stripe/credit_card_cloner'
module Stripe
describe CreditCardCloner do
describe "#clone!" do
describe "#clone" do
let(:cloner) { Stripe::CreditCardCloner.new }
let(:customer_id) { "cus_A123" }
@@ -14,24 +14,32 @@ module Stripe
let(:new_payment_method_id) { "pm_4567" }
let(:stripe_account_id) { "acct_456" }
let(:customer_response_mock) { { status: 200, body: customer_response_body } }
let(:payment_method_response_mock) { { status: 200, body: payment_method_response_body } }
let(:credit_card) { create(:credit_card, user: create(:user)) }
before do
allow(Stripe).to receive(:api_key) { "sk_test_12345" }
stub_request(:post, "https://api.stripe.com/v1/payment_methods")
.with(basic_auth: ["sk_test_12345", ""])
.to_return(payment_method_response_mock)
stub_request(:post, "https://api.stripe.com/v1/customers")
.with(basic_auth: ["sk_test_12345", ""], body: { email: credit_card.user.email })
.to_return(customer_response_mock)
end
context "when called with a credit_card with valid id (card_*)" do
let(:payment_method_response_body) {
JSON.generate(id: new_payment_method_id, default_card: card_id)
}
let(:customer_response_body) {
JSON.generate(id: customer_id, default_card: card_id)
}
it "clones the card successefully" do
cloner.clone!(credit_card, stripe_account_id)
cloner.clone(credit_card, stripe_account_id)
expect(credit_card.gateway_customer_profile_id).to eq new_customer_id
expect(credit_card.gateway_payment_profile_id).to eq new_payment_method_id