mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-31 21:37:16 +00:00
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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user