зеркало из https://github.com/mozilla/fxa.git
fix(subscriptions): Duplicate charge on cvc fail (#12046)
* fix(subscriptions): Duplicate charge on cvc fail Because: * An incorrect card cvc isn't always immediately caught, which causes an incomplete subscription. Once the customer corrects the cvc it could happen that a new subscription is created, instead of completing the existing subscription. As a result, both subscriptions are completed and the card is charged twice. This commit: * Ensures that only 1 subscription is created and the card is only charged once. Closes #11024
This commit is contained in:
Родитель
f31ec81756
Коммит
b5b3856104
|
@ -487,6 +487,21 @@ export class StripeHelper {
|
|||
},
|
||||
{ idempotencyKey: `ssc-${subIdempotencyKey}` }
|
||||
);
|
||||
|
||||
const paymentIntent = (subscription.latest_invoice as Stripe.Invoice)
|
||||
.payment_intent as Stripe.PaymentIntent;
|
||||
|
||||
if (paymentIntent?.last_payment_error) {
|
||||
await this.cancelSubscription(subscription.id);
|
||||
|
||||
throw error.rejectedSubscriptionPaymentToken(
|
||||
paymentIntent.last_payment_error.code,
|
||||
new Error(
|
||||
`Subscription creation failed with error code ${paymentIntent.last_payment_error.code}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const updatedSubscription = await this.postSubscriptionCreationUpdates({
|
||||
subscription,
|
||||
promotionCode,
|
||||
|
|
|
@ -0,0 +1,498 @@
|
|||
{
|
||||
"id": "sub_1LLsX2JEcmKzuWtRqw2US2v6",
|
||||
"object": "subscription",
|
||||
"application_fee_percent": null,
|
||||
"automatic_tax": {
|
||||
"enabled": false
|
||||
},
|
||||
"billing_cycle_anchor": 1643130431,
|
||||
"billing_thresholds": null,
|
||||
"cancel_at": null,
|
||||
"cancel_at_period_end": false,
|
||||
"canceled_at": null,
|
||||
"collection_method": "charge_automatically",
|
||||
"created": 1643130431,
|
||||
"current_period_end": 1645808831,
|
||||
"current_period_start": 1643130431,
|
||||
"customer": "cus_E1wCpqeIZU2lcK",
|
||||
"days_until_due": null,
|
||||
"default_payment_method": null,
|
||||
"default_source": null,
|
||||
"default_tax_rates": [],
|
||||
"discount": null,
|
||||
"ended_at": null,
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_E1wItgKCx1KK29",
|
||||
"object": "subscription_item",
|
||||
"billing_thresholds": null,
|
||||
"created": 1643130432,
|
||||
"metadata": {},
|
||||
"plan": {
|
||||
"id": "price_2Iq6qKLNrqKwuEkKJICpIwKj",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 999,
|
||||
"amount_decimal": "999",
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1622216612,
|
||||
"currency": "usd",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": true,
|
||||
"metadata": {
|
||||
"downloadURL": "https://vpn.mozilla.org/vpn/download?utm_medium=email&utm_source=email&utm_campaign=subscription-download",
|
||||
"webIconURL": "https://accounts-static.cdn.mozilla.net/product-icons/mozilla-vpn-web.svg"
|
||||
},
|
||||
"nickname": "VPN Wave 1 Monthly",
|
||||
"product": "prod_KvrtYEIwqzn2YU",
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"price": {
|
||||
"id": "price_2Iq6qKLNrqKwuEkKJICpIwKj",
|
||||
"object": "price",
|
||||
"active": true,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1622216612,
|
||||
"currency": "usd",
|
||||
"livemode": true,
|
||||
"lookup_key": null,
|
||||
"metadata": {
|
||||
"downloadURL": "https://vpn.mozilla.org/vpn/download?utm_medium=email&utm_source=email&utm_campaign=subscription-download",
|
||||
"webIconURL": "https://accounts-static.cdn.mozilla.net/product-icons/mozilla-vpn-web.svg"
|
||||
},
|
||||
"nickname": "VPN Wave 1 Monthly",
|
||||
"product": "prod_KvrtYEIwqzn2YU",
|
||||
"recurring": {
|
||||
"aggregate_usage": null,
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"tax_behavior": "unspecified",
|
||||
"tiers_mode": null,
|
||||
"transform_quantity": null,
|
||||
"type": "recurring",
|
||||
"unit_amount": 999,
|
||||
"unit_amount_decimal": "999"
|
||||
},
|
||||
"quantity": 1,
|
||||
"subscription": "sub_1LLsX2JEcmKzuWtRqw2US2v6",
|
||||
"tax_rates": []
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_1LLsX2JEcmKzuWtRqw2US2v6"
|
||||
},
|
||||
"latest_invoice": {
|
||||
"id": "in_4HJqU2HNqmGzyKtWChfyj03k",
|
||||
"object": "invoice",
|
||||
"account_country": "US",
|
||||
"account_name": "Mozilla Corporation",
|
||||
"account_tax_ids": null,
|
||||
"amount_due": 999,
|
||||
"amount_paid": 0,
|
||||
"amount_remaining": 999,
|
||||
"application_fee_amount": null,
|
||||
"attempt_count": 1,
|
||||
"attempted": true,
|
||||
"auto_advance": true,
|
||||
"automatic_tax": {
|
||||
"enabled": false,
|
||||
"status": null
|
||||
},
|
||||
"billing_reason": "subscription_create",
|
||||
"charge": "ch_1SKsU2JWvtRquNtE2q20Lqn1",
|
||||
"collection_method": "charge_automatically",
|
||||
"created": 1643130432,
|
||||
"currency": "usd",
|
||||
"custom_fields": null,
|
||||
"customer": "cus_E1wCpqeIZU2lcK",
|
||||
"customer_address": null,
|
||||
"customer_email": "sonnypeter@example.com",
|
||||
"customer_name": "Sonny Peter",
|
||||
"customer_phone": null,
|
||||
"customer_shipping": null,
|
||||
"customer_tax_exempt": "none",
|
||||
"customer_tax_ids": [],
|
||||
"default_payment_method": null,
|
||||
"default_source": null,
|
||||
"default_tax_rates": [],
|
||||
"description": null,
|
||||
"discount": null,
|
||||
"discounts": [],
|
||||
"due_date": null,
|
||||
"ending_balance": 0,
|
||||
"footer": null,
|
||||
"hosted_invoice_url": "",
|
||||
"invoice_pdf": "",
|
||||
"last_finalization_error": null,
|
||||
"lines": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "il_5TLsE8MKrtPzuCtUxewBKPPM",
|
||||
"object": "line_item",
|
||||
"amount": 999,
|
||||
"currency": "usd",
|
||||
"description": "1 × Mozilla VPN (at $9.99 / month)",
|
||||
"discount_amounts": [],
|
||||
"discountable": true,
|
||||
"discounts": [],
|
||||
"livemode": true,
|
||||
"metadata": {},
|
||||
"period": {
|
||||
"end": 1645808831,
|
||||
"start": 1643130431
|
||||
},
|
||||
"plan": {
|
||||
"id": "price_2Iq6qKLNrqKwuEkKJICpIwKj",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 999,
|
||||
"amount_decimal": "999",
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1622216612,
|
||||
"currency": "usd",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": true,
|
||||
"metadata": {
|
||||
"downloadURL": "https://vpn.mozilla.org/vpn/download?utm_medium=email&utm_source=email&utm_campaign=subscription-download",
|
||||
"webIconURL": "https://accounts-static.cdn.mozilla.net/product-icons/mozilla-vpn-web.svg"
|
||||
},
|
||||
"nickname": "VPN Wave 1 Monthly",
|
||||
"product": "prod_KvrtYEIwqzn2YU",
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"price": {
|
||||
"id": "price_2Iq6qKLNrqKwuEkKJICpIwKj",
|
||||
"object": "price",
|
||||
"active": true,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1622216612,
|
||||
"currency": "usd",
|
||||
"livemode": true,
|
||||
"lookup_key": null,
|
||||
"metadata": {
|
||||
"downloadURL": "https://vpn.mozilla.org/vpn/download?utm_medium=email&utm_source=email&utm_campaign=subscription-download",
|
||||
"webIconURL": "https://accounts-static.cdn.mozilla.net/product-icons/mozilla-vpn-web.svg"
|
||||
},
|
||||
"nickname": "VPN Wave 1 Monthly",
|
||||
"product": "prod_KvrtYEIwqzn2YU",
|
||||
"recurring": {
|
||||
"aggregate_usage": null,
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"tax_behavior": "unspecified",
|
||||
"tiers_mode": null,
|
||||
"transform_quantity": null,
|
||||
"type": "recurring",
|
||||
"unit_amount": 999,
|
||||
"unit_amount_decimal": "999"
|
||||
},
|
||||
"proration": false,
|
||||
"quantity": 1,
|
||||
"subscription": "sub_1LLsX2JEcmKzuWtRqw2US2v6",
|
||||
"subscription_item": "si_E1wItgKCx1KK29",
|
||||
"tax_amounts": [],
|
||||
"tax_rates": [],
|
||||
"type": "subscription"
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/invoices/in_4HJqU2HNqmGzyKtWChfyj03k/lines"
|
||||
},
|
||||
"livemode": true,
|
||||
"metadata": {},
|
||||
"next_payment_attempt": null,
|
||||
"number": "1348121I-0001",
|
||||
"on_behalf_of": null,
|
||||
"paid": false,
|
||||
"paid_out_of_band": false,
|
||||
"payment_intent": {
|
||||
"id": "pi_2YLsU2JNcmKzuEtC39sJeUhk",
|
||||
"object": "payment_intent",
|
||||
"amount": 999,
|
||||
"amount_capturable": 0,
|
||||
"amount_received": 0,
|
||||
"application": null,
|
||||
"application_fee_amount": null,
|
||||
"automatic_payment_methods": null,
|
||||
"canceled_at": null,
|
||||
"cancellation_reason": null,
|
||||
"capture_method": "automatic",
|
||||
"charges": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "ch_1SKsU2JWvtRquNtE2q20Lqn1",
|
||||
"object": "charge",
|
||||
"amount": 999,
|
||||
"amount_captured": 0,
|
||||
"amount_refunded": 0,
|
||||
"application": null,
|
||||
"application_fee": null,
|
||||
"application_fee_amount": null,
|
||||
"balance_transaction": null,
|
||||
"billing_details": {
|
||||
"address": {
|
||||
"city": null,
|
||||
"country": null,
|
||||
"line1": null,
|
||||
"line2": null,
|
||||
"postal_code": "78701",
|
||||
"state": null
|
||||
},
|
||||
"email": null,
|
||||
"name": null,
|
||||
"phone": null
|
||||
},
|
||||
"calculated_statement_descriptor": "MOZILLA VPN",
|
||||
"captured": false,
|
||||
"created": 1643130432,
|
||||
"currency": "usd",
|
||||
"customer": "cus_E1wCpqeIZU2lcK",
|
||||
"description": "Subscription creation",
|
||||
"destination": null,
|
||||
"dispute": null,
|
||||
"disputed": false,
|
||||
"failure_code": "incorrect_cvc",
|
||||
"failure_message": "Your card's security code is incorrect.",
|
||||
"fraud_details": {},
|
||||
"invoice": "in_4HJqU2HNqmGzyKtWChfyj03k",
|
||||
"livemode": true,
|
||||
"metadata": {},
|
||||
"on_behalf_of": null,
|
||||
"order": null,
|
||||
"outcome": {
|
||||
"network_status": "reversed_after_approval",
|
||||
"reason": "requested_block_on_incorrect_cvc",
|
||||
"risk_level": "normal",
|
||||
"risk_score": 0,
|
||||
"rule": "block_if_wrong_cvc",
|
||||
"seller_message": "You requested that Stripe block payments (like this one) for which the customer-entered CVC code does not match the code on file with the card-issuing bank.",
|
||||
"type": "blocked"
|
||||
},
|
||||
"paid": false,
|
||||
"payment_intent": "pi_2YLsU2JNcmKzuEtC39sJeUhk",
|
||||
"payment_method": "pm_5SLsU2ERltPueCtERxUYcltT",
|
||||
"payment_method_details": {
|
||||
"card": {
|
||||
"brand": "visa",
|
||||
"checks": {
|
||||
"address_line1_check": null,
|
||||
"address_postal_code_check": "pass",
|
||||
"cvc_check": "fail"
|
||||
},
|
||||
"country": "US",
|
||||
"exp_month": 5,
|
||||
"exp_year": 2026,
|
||||
"fingerprint": "r23vKuyhEQrEKxKm",
|
||||
"funding": "credit",
|
||||
"installments": null,
|
||||
"last4": "3808",
|
||||
"network": "visa",
|
||||
"three_d_secure": null,
|
||||
"wallet": null
|
||||
},
|
||||
"type": "card"
|
||||
},
|
||||
"receipt_email": null,
|
||||
"receipt_number": null,
|
||||
"receipt_url": null,
|
||||
"refunded": false,
|
||||
"refunds": {
|
||||
"object": "list",
|
||||
"data": [],
|
||||
"has_more": false,
|
||||
"total_count": 0,
|
||||
"url": "/v1/charges/ch_1SKsU2JWvtRquNtE2q20Lqn1/refunds"
|
||||
},
|
||||
"review": null,
|
||||
"shipping": null,
|
||||
"source": null,
|
||||
"source_transfer": null,
|
||||
"statement_descriptor": "Mozilla VPN",
|
||||
"statement_descriptor_suffix": null,
|
||||
"status": "failed",
|
||||
"transfer_data": null,
|
||||
"transfer_group": null
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/charges?payment_intent=pi_2YLsU2JNcmKzuEtC39sJeUhk"
|
||||
},
|
||||
"client_secret": "pi_2YLsU2JNcmKzuEtC39sJeUhk_secret_IuXaPD13KJU8AmIkCWh66tbzO",
|
||||
"confirmation_method": "automatic",
|
||||
"created": 1643130432,
|
||||
"currency": "usd",
|
||||
"customer": "cus_E1wCpqeIZU2lcK",
|
||||
"description": "Subscription creation",
|
||||
"invoice": "in_4HJqU2HNqmGzyKtWChfyj03k",
|
||||
"last_payment_error": {
|
||||
"charge": "ch_1SKsU2JWvtRquNtE2q20Lqn1",
|
||||
"code": "incorrect_cvc",
|
||||
"doc_url": "https://stripe.com/docs/error-codes/incorrect-cvc",
|
||||
"message": "Your card's security code is incorrect.",
|
||||
"param": "cvc",
|
||||
"payment_method": {
|
||||
"id": "pm_5SLsU2ERltPueCtERxUYcltT",
|
||||
"object": "payment_method",
|
||||
"billing_details": {
|
||||
"address": {
|
||||
"city": null,
|
||||
"country": null,
|
||||
"line1": null,
|
||||
"line2": null,
|
||||
"postal_code": "78701",
|
||||
"state": null
|
||||
},
|
||||
"email": null,
|
||||
"name": null,
|
||||
"phone": null
|
||||
},
|
||||
"card": {
|
||||
"brand": "visa",
|
||||
"checks": {
|
||||
"address_line1_check": null,
|
||||
"address_postal_code_check": "pass",
|
||||
"cvc_check": "fail"
|
||||
},
|
||||
"country": "US",
|
||||
"exp_month": 5,
|
||||
"exp_year": 2026,
|
||||
"fingerprint": "r23vKuyhEQrEKxKm",
|
||||
"funding": "credit",
|
||||
"generated_from": null,
|
||||
"last4": "3808",
|
||||
"networks": {
|
||||
"available": ["visa"],
|
||||
"preferred": null
|
||||
},
|
||||
"three_d_secure_usage": {
|
||||
"supported": true
|
||||
},
|
||||
"wallet": null
|
||||
},
|
||||
"created": 1643130428,
|
||||
"customer": "cus_E1wCpqeIZU2lcK",
|
||||
"livemode": true,
|
||||
"metadata": {},
|
||||
"type": "card"
|
||||
},
|
||||
"type": "card_error"
|
||||
},
|
||||
"livemode": true,
|
||||
"metadata": {},
|
||||
"next_action": null,
|
||||
"on_behalf_of": null,
|
||||
"payment_method": null,
|
||||
"payment_method_options": {
|
||||
"card": {
|
||||
"installments": null,
|
||||
"network": null,
|
||||
"request_three_d_secure": "automatic"
|
||||
}
|
||||
},
|
||||
"payment_method_types": ["card"],
|
||||
"processing": null,
|
||||
"receipt_email": null,
|
||||
"review": null,
|
||||
"setup_future_usage": "off_session",
|
||||
"shipping": null,
|
||||
"source": null,
|
||||
"statement_descriptor": "Mozilla VPN",
|
||||
"statement_descriptor_suffix": null,
|
||||
"status": "requires_payment_method",
|
||||
"transfer_data": null,
|
||||
"transfer_group": null
|
||||
},
|
||||
"payment_settings": {
|
||||
"payment_method_options": null,
|
||||
"payment_method_types": null
|
||||
},
|
||||
"period_end": 1643130431,
|
||||
"period_start": 1643130431,
|
||||
"post_payment_credit_notes_amount": 0,
|
||||
"pre_payment_credit_notes_amount": 0,
|
||||
"quote": null,
|
||||
"receipt_number": null,
|
||||
"starting_balance": 0,
|
||||
"statement_descriptor": null,
|
||||
"status": "open",
|
||||
"status_transitions": {
|
||||
"finalized_at": 1643130431,
|
||||
"marked_uncollectible_at": null,
|
||||
"paid_at": null,
|
||||
"voided_at": null
|
||||
},
|
||||
"subscription": "sub_1LLsX2JEcmKzuWtRqw2US2v6",
|
||||
"subtotal": 999,
|
||||
"tax": null,
|
||||
"total": 999,
|
||||
"total_discount_amounts": [],
|
||||
"total_tax_amounts": [],
|
||||
"transfer_data": null,
|
||||
"webhooks_delivered_at": null
|
||||
},
|
||||
"livemode": true,
|
||||
"metadata": {},
|
||||
"next_pending_invoice_item_invoice": null,
|
||||
"pause_collection": null,
|
||||
"payment_settings": {
|
||||
"payment_method_options": null,
|
||||
"payment_method_types": null
|
||||
},
|
||||
"pending_invoice_item_interval": null,
|
||||
"pending_setup_intent": null,
|
||||
"pending_update": null,
|
||||
"plan": {
|
||||
"id": "price_2Iq6qKLNrqKwuEkKJICpIwKj",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"aggregate_usage": null,
|
||||
"amount": 999,
|
||||
"amount_decimal": "999",
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1622216612,
|
||||
"currency": "usd",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": true,
|
||||
"metadata": {
|
||||
"downloadURL": "https://vpn.mozilla.org/vpn/download?utm_medium=email&utm_source=email&utm_campaign=subscription-download",
|
||||
"webIconURL": "https://accounts-static.cdn.mozilla.net/product-icons/mozilla-vpn-web.svg"
|
||||
},
|
||||
"nickname": "VPN Wave 1 Monthly",
|
||||
"product": "prod_KvrtYEIwqzn2YU",
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"schedule": null,
|
||||
"start_date": 1643130431,
|
||||
"status": "incomplete",
|
||||
"transfer_data": null,
|
||||
"trial_end": null,
|
||||
"trial_start": null
|
||||
}
|
|
@ -52,6 +52,7 @@ const subscription1 = require('./fixtures/stripe/subscription1.json');
|
|||
const subscription2 = require('./fixtures/stripe/subscription2.json');
|
||||
const multiPlanSubscription = require('./fixtures/stripe/subscription_multiplan.json');
|
||||
const subscriptionPMIExpanded = require('./fixtures/stripe/subscription_pmi_expanded.json');
|
||||
const subscriptionPMIExpandedIncompleteCVCFail = require('./fixtures/stripe/subscription_pmi_expanded_incomplete_cvc_fail.json');
|
||||
const cancelledSubscription = require('./fixtures/stripe/subscription_cancelled.json');
|
||||
const pastDueSubscription = require('./fixtures/stripe/subscription_past_due.json');
|
||||
const paidInvoice = require('./fixtures/stripe/invoice_paid.json');
|
||||
|
@ -822,6 +823,64 @@ describe('StripeHelper', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('errors and deletes subscription when a cvc check fails on subscription creation', async () => {
|
||||
const attachExpected = deepCopy(paymentMethodAttach);
|
||||
const customerExpected = deepCopy(newCustomerPM);
|
||||
sandbox
|
||||
.stub(stripeHelper.stripe.paymentMethods, 'attach')
|
||||
.resolves(attachExpected);
|
||||
sandbox
|
||||
.stub(stripeHelper.stripe.customers, 'update')
|
||||
.resolves(customerExpected);
|
||||
sandbox
|
||||
.stub(stripeHelper.stripe.subscriptions, 'create')
|
||||
.resolves(subscriptionPMIExpandedIncompleteCVCFail);
|
||||
sandbox.stub(stripeHelper, 'cancelSubscription').resolves({});
|
||||
const subIdempotencyKey = uuidv4();
|
||||
stripeFirestore.insertCustomerRecordWithBackfill = sandbox
|
||||
.stub()
|
||||
.resolves({});
|
||||
stripeFirestore.insertSubscriptionRecordWithBackfill = sandbox
|
||||
.stub()
|
||||
.resolves({});
|
||||
stripeFirestore.insertPaymentMethodRecord = sandbox.stub().resolves({});
|
||||
|
||||
try {
|
||||
await stripeHelper.createSubscriptionWithPMI({
|
||||
customerId: 'customerId',
|
||||
priceId: 'priceId',
|
||||
paymentMethodId: 'pm_1H0FRp2eZvKYlo2CeIZoc0wj',
|
||||
subIdempotencyKey,
|
||||
taxRateId: 'tr_asdf',
|
||||
});
|
||||
sinon.assert.fail();
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.errno,
|
||||
error.ERRNO.REJECTED_SUBSCRIPTION_PAYMENT_TOKEN
|
||||
);
|
||||
}
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
stripeHelper.stripe.subscriptions.create,
|
||||
{
|
||||
customer: 'customerId',
|
||||
items: [{ price: 'priceId' }],
|
||||
expand: ['latest_invoice.payment_intent'],
|
||||
default_tax_rates: ['tr_asdf'],
|
||||
promotion_code: undefined,
|
||||
},
|
||||
{ idempotencyKey: `ssc-${subIdempotencyKey}` }
|
||||
);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
stripeHelper.cancelSubscription,
|
||||
subscriptionPMIExpandedIncompleteCVCFail.id
|
||||
);
|
||||
sinon.assert.notCalled(
|
||||
stripeFirestore.insertSubscriptionRecordWithBackfill
|
||||
);
|
||||
sinon.assert.callCount(mockStatsd.increment, 1);
|
||||
});
|
||||
|
||||
it('surfaces payment issues', async () => {
|
||||
const apiError = new stripeError.StripeCardError();
|
||||
sandbox
|
||||
|
|
Загрузка…
Ссылка в новой задаче