:PROPERTIES: :ID: 7d940785-68b9-4da7-bad1-4771d496168c :END: #+title: Stripe API #+PROPERTY: header-args:shell :results output #+PROPERTY: header-args :cache yes :exports both :var account="acct_1IGnMkIoFf3wvXpR" :eval no-export #+TOC: headlines 2 * Product #+name: product #+begin_src shell :results output stripe products create \ --stripe-account=$account \ --name="Example Product (Org-Roam Doc)" \ -d "images[0]"="https://www.orgroam.com/img/logo.png" \ | jq -r .id #+end_src #+RESULTS[0094337876c9d1d55bdfd716fda03da4d71e6e9c]: product : prod_JBeWfGHNqBcy5B ** Non-Recurring Price #+name: price-single #+begin_src shell :var product=product stripe prices create \ --stripe-account=$account \ --product=$product \ --unit-amount=-1 \ --currency=jpy \ -d "nickname"="Non-Recurring" #+end_src #+RESULTS[d9b883e9f85ada78e689aa559e5488f41118026c]: price-single : { : "error": { : "message": "This value must be less than or equal to 999999999999 (it currently is '9999999999991').", : "param": "unit_amount", : "type": "invalid_request_error" : } : } ** Recurring Price #+name: price-recurring #+begin_src shell :var product=product stripe prices create \ --stripe-account=$account \ --product=$product \ --unit-amount=999 \ --currency=usd \ -d "nickname"="Recurring" \ -d "recurring[interval]"="month" \ | jq -r .id #+end_src #+RESULTS[8e8d2f69d8521128b6f69143d933ac173a9d9433]: price-recurring : price_1IZHFZIoFf3wvXpRtHW5EKFo * Customer #+name: customer #+begin_src shell :results output stripe customers create \ --stripe-account=$account \ --description="Org Roam Docs Customer" \ --name="Correl Roush" \ --email="correl+stripe.roam.docs@gmail.com" \ | jq -r .id #+end_src #+RESULTS[c85b560e232abaac68423ce28b71029a7146c021]: customer : cus_JBeXOsWSmfXrDX * One-Time Invoicing We set up a purchase for the client to then complete by confirming the payment intent with payment information. ** Create invoice item #+NAME: otp-invoiceitem #+HEADER: :var customer=customer #+HEADER: :var price=price-single #+begin_src shell stripe invoiceitems create \ --stripe-account=$account \ --customer=$customer \ --price=$price \ | jq -r .id #+end_src #+RESULTS[a475da86ff77f519ee5bd8b928377c88b696abb8]: otp-invoiceitem : ii_1IZHGHIoFf3wvXpRnP7xBsbd ** Create invoice #+NAME: otp-invoice #+HEADER: :var customer=customer #+begin_src shell stripe invoices create \ --stripe-account="$account" \ --customer="$customer" \ | jq -r .id #+end_src #+RESULTS[12e688bd6def0039295ea18f1a38482c12c2a9ad]: otp-invoice : in_1IZHGOIoFf3wvXpRuP2Og77y #+NAME: otp-intent #+HEADER: :var invoice=otp-invoice #+begin_src shell stripe invoices finalize_invoice \ --stripe-account=$account \ $invoice #+end_src #+RESULTS[e5aeed338fa41b3b018af508fee5eae83942a255]: otp-intent #+begin_example { "id": "in_1IZHGOIoFf3wvXpRuP2Og77y", "object": "invoice", "account_country": "US", "account_name": "Correl's Stuff", "account_tax_ids": null, "amount_due": 350, "amount_paid": 0, "amount_remaining": 350, "application_fee_amount": null, "attempt_count": 0, "attempted": false, "auto_advance": false, "billing_reason": "manual", "charge": null, "collection_method": "charge_automatically", "created": 1616771272, "currency": "usd", "custom_fields": null, "customer": "cus_JBeXOsWSmfXrDX", "customer_address": null, "customer_email": "correl+stripe.roam.docs@gmail.com", "customer_name": "Correl Roush", "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": "https://invoice.stripe.com/i/acct_1IGnMkIoFf3wvXpR/invst_JBeZpwIUNWNhG3JLRP5QBZ2RpKeDs8t", "invoice_pdf": "https://pay.stripe.com/invoice/acct_1IGnMkIoFf3wvXpR/invst_JBeZpwIUNWNhG3JLRP5QBZ2RpKeDs8t/pdf", "last_finalization_error": null, "lines": { "object": "list", "data": [ { "id": "il_1IZHGHIoFf3wvXpRMwwv5nhW", "object": "line_item", "amount": 350, "currency": "usd", "description": "Example Product (Org-Roam Doc)", "discount_amounts": [ ], "discountable": true, "discounts": [ ], "invoice_item": "ii_1IZHGHIoFf3wvXpRnP7xBsbd", "livemode": false, "metadata": { }, "period": { "end": 1616771265, "start": 1616771265 }, "plan": null, "price": { "id": "price_1IZHFSIoFf3wvXpRHwH3GRm8", "object": "price", "active": true, "billing_scheme": "per_unit", "created": 1616771214, "currency": "usd", "livemode": false, "lookup_key": null, "metadata": { }, "nickname": "Non-Recurring", "product": "prod_JBeWfGHNqBcy5B", "recurring": null, "tiers_mode": null, "transform_quantity": null, "type": "one_time", "unit_amount": 350, "unit_amount_decimal": "350" }, "proration": false, "quantity": 1, "subscription": null, "tax_amounts": [ ], "tax_rates": [ ], "type": "invoiceitem" } ], "has_more": false, "total_count": 1, "url": "/v1/invoices/in_1IZHGOIoFf3wvXpRuP2Og77y/lines" }, "livemode": false, "metadata": { }, "next_payment_attempt": null, "number": "0EBAE885-0001", "on_behalf_of": null, "paid": false, "payment_intent": "pi_1IZHH8IoFf3wvXpRKYVCxVtO", "payment_settings": { "payment_method_options": null, "payment_method_types": null }, "period_end": 1616771272, "period_start": 1616771272, "post_payment_credit_notes_amount": 0, "pre_payment_credit_notes_amount": 0, "receipt_number": null, "starting_balance": 0, "statement_descriptor": null, "status": "open", "status_transitions": { "finalized_at": 1616771318, "marked_uncollectible_at": null, "paid_at": null, "voided_at": null }, "subscription": null, "subtotal": 350, "tax": null, "total": 350, "total_discount_amounts": [ ], "total_tax_amounts": [ ], "transfer_data": null, "webhooks_delivered_at": 1616771274 } #+end_example ** Pay the invoice #+HEADER: :var intent="pi_1IZG0gIoFf3wvXpRMv4JIJlv" #+begin_src shell stripe payment_intents confirm $intent \ --payment-method=pm_card_visa #+end_src #+RESULTS[2cd37055813d6711442f7462d1d7e6338dce3ab7]: #+begin_example { "id": "pi_1IZG0gIoFf3wvXpRMv4JIJlv", "object": "payment_intent", "amount": 350, "amount_capturable": 0, "amount_received": 350, "application": null, "application_fee_amount": null, "canceled_at": null, "cancellation_reason": null, "capture_method": "automatic", "charges": { "object": "list", "data": [ { "id": "ch_1IZG1rIoFf3wvXpRVFw1lFw7", "object": "charge", "amount": 350, "amount_captured": 350, "amount_refunded": 0, "application": null, "application_fee": null, "application_fee_amount": null, "balance_transaction": "txn_1IZG1rIoFf3wvXpR7ROwaORh", "billing_details": { "address": { "city": null, "country": null, "line1": null, "line2": null, "postal_code": null, "state": null }, "email": null, "name": null, "phone": null }, "calculated_statement_descriptor": "CORRELS STUFF", "captured": true, "created": 1616766527, "currency": "usd", "customer": "cus_JBNQ85gmNWdWB3", "description": "Payment for Invoice", "destination": null, "dispute": null, "disputed": false, "failure_code": null, "failure_message": null, "fraud_details": { }, "invoice": "in_1IZG0YIoFf3wvXpR6aGs4GtD", "livemode": false, "metadata": { }, "on_behalf_of": null, "order": null, "outcome": { "network_status": "approved_by_network", "reason": null, "risk_level": "normal", "risk_score": 39, "seller_message": "Payment complete.", "type": "authorized" }, "paid": true, "payment_intent": "pi_1IZG0gIoFf3wvXpRMv4JIJlv", "payment_method": "pm_1IZG1qIoFf3wvXpRX3NUaXbF", "payment_method_details": { "card": { "brand": "visa", "checks": { "address_line1_check": null, "address_postal_code_check": null, "cvc_check": null }, "country": "US", "exp_month": 3, "exp_year": 2022, "fingerprint": "ueUYSX2U2UUDmYpO", "funding": "credit", "installments": null, "last4": "4242", "network": "visa", "three_d_secure": null, "wallet": null }, "type": "card" }, "receipt_email": null, "receipt_number": null, "receipt_url": "https://pay.stripe.com/receipts/acct_1IGnMkIoFf3wvXpR/ch_1IZG1rIoFf3wvXpRVFw1lFw7/rcpt_JBdIhBejsEXLYLJMLjteIt8UWMMh5md", "refunded": false, "refunds": { "object": "list", "data": [ ], "has_more": false, "total_count": 0, "url": "/v1/charges/ch_1IZG1rIoFf3wvXpRVFw1lFw7/refunds" }, "review": null, "shipping": null, "source": null, "source_transfer": null, "statement_descriptor": null, "statement_descriptor_suffix": null, "status": "succeeded", "transfer_data": null, "transfer_group": null } ], "has_more": false, "total_count": 1, "url": "/v1/charges?payment_intent=pi_1IZG0gIoFf3wvXpRMv4JIJlv" }, "client_secret": "pi_1IZG0gIoFf3wvXpRMv4JIJlv_secret_48XYkW8i6bDhBOtA7AE6R9drd", "confirmation_method": "automatic", "created": 1616766454, "currency": "usd", "customer": "cus_JBNQ85gmNWdWB3", "description": "Payment for Invoice", "invoice": "in_1IZG0YIoFf3wvXpR6aGs4GtD", "last_payment_error": null, "livemode": false, "metadata": { }, "next_action": null, "on_behalf_of": null, "payment_method": "pm_1IZG1qIoFf3wvXpRX3NUaXbF", "payment_method_options": { "card": { "installments": null, "network": null, "request_three_d_secure": "automatic" } }, "payment_method_types": [ "card" ], "receipt_email": null, "review": null, "setup_future_usage": null, "shipping": null, "source": null, "statement_descriptor": null, "statement_descriptor_suffix": null, "status": "succeeded", "transfer_data": null, "transfer_group": null } #+end_example * One-Time Invoicing (Payment method first) By having the client prepare the payment intent before requesting the purchase, we can handle invoicing and completing the purchase on our end without further input from the client. ** Create a payment method #+NAME: payment-method #+begin_src shell stripe payment_methods create \ --stripe-account=$account \ --type=card \ -d "card[number]"=4242424242424242 \ -d "card[exp_month]"=3 \ -d "card[exp_year]"=2022 \ -d "card[cvc]"=314 \ | jq -r '.id' #+end_src #+RESULTS[df876e7c97be477440f418bec23e78b376d86944]: payment-method : pm_1J5FujIoFf3wvXpRgemyYO93 ** Attach the payment method to the customer #+HEADER: :var customer=customer #+HEADER: :var payment_method=payment-method #+begin_src shell stripe payment_methods attach "$payment_method" \ --stripe-account=$account \ --customer="$customer" #+end_src #+RESULTS[4290d9f7fe1f6c7362c61268ca3da026798046d0]: #+begin_example { "id": "pm_1IZHILIoFf3wvXpR3oRjPoMx", "object": "payment_method", "billing_details": { "address": { "city": null, "country": null, "line1": null, "line2": null, "postal_code": null, "state": null }, "email": null, "name": null, "phone": null }, "card": { "brand": "visa", "checks": { "address_line1_check": null, "address_postal_code_check": null, "cvc_check": "pass" }, "country": "US", "exp_month": 3, "exp_year": 2022, "fingerprint": "C5qD8oSCGvVCbtcH", "funding": "credit", "generated_from": null, "last4": "4242", "networks": { "available": [ "visa" ], "preferred": null }, "three_d_secure_usage": { "supported": true }, "wallet": null }, "created": 1616771394, "customer": "cus_JBeXOsWSmfXrDX", "livemode": false, "metadata": { }, "type": "card" } #+end_example ** Create invoice item #+NAME: otp2-invoiceitem #+HEADER: :var customer=customer #+HEADER: :var price=price-single #+begin_src shell stripe invoiceitems create \ --stripe-account=$account \ --customer=$customer \ --price=$price \ | jq -r .id #+end_src #+RESULTS[a475da86ff77f519ee5bd8b928377c88b696abb8]: otp2-invoiceitem : ii_1IZJF0IoFf3wvXpROwCaDRo0 ** Create invoice #+NAME: otp2-invoice #+HEADER: :var customer=customer #+HEADER: :var payment_method="pm_1IZHILIoFf3wvXpR3oRjPoMx" #+begin_src shell stripe invoices create \ --stripe-account="$account" \ --customer="$customer" \ --auto-advance=false \ -d "application_fee_amount"=35 \ -d "default_payment_method"=$payment_method \ | jq -r .id #+end_src #+RESULTS[aa892f23ee3bedd4ce813b435a49ef440a64b3ed]: otp2-invoice : in_1IZJF5IoFf3wvXpRu9PnTZIl ** Pay Invoice #+NAME: otp2-payment #+HEADER: :var invoice=otp2-invoice #+begin_src shell stripe invoices pay \ --stripe-account=$account \ $invoice #+end_src #+RESULTS[269ab4684e04178d7081e0d614e579f925369798]: otp2-payment #+begin_example { "id": "in_1IZJF5IoFf3wvXpRu9PnTZIl", "object": "invoice", "account_country": "US", "account_name": "Correl's Stuff", "account_tax_ids": null, "amount_due": 350, "amount_paid": 350, "amount_remaining": 0, "application_fee_amount": 35, "attempt_count": 1, "attempted": true, "auto_advance": false, "billing_reason": "manual", "charge": "ch_1IZJFdIoFf3wvXpRfCT89Njs", "collection_method": "charge_automatically", "created": 1616778879, "currency": "usd", "custom_fields": null, "customer": "cus_JBeXOsWSmfXrDX", "customer_address": null, "customer_email": "correl+stripe.roam.docs@gmail.com", "customer_name": "Correl Roush", "customer_phone": null, "customer_shipping": null, "customer_tax_exempt": "none", "customer_tax_ids": [ ], "default_payment_method": "pm_1IZHILIoFf3wvXpR3oRjPoMx", "default_source": null, "default_tax_rates": [ ], "description": null, "discount": null, "discounts": [ ], "due_date": null, "ending_balance": 0, "footer": null, "hosted_invoice_url": "https://invoice.stripe.com/i/acct_1IGnMkIoFf3wvXpR/invst_JBgcIFGvkWp3ZnI1HxNWwAZl7ccRzbY", "invoice_pdf": "https://pay.stripe.com/invoice/acct_1IGnMkIoFf3wvXpR/invst_JBgcIFGvkWp3ZnI1HxNWwAZl7ccRzbY/pdf", "last_finalization_error": null, "lines": { "object": "list", "data": [ { "id": "il_1IZJF0IoFf3wvXpRdhrY6NFl", "object": "line_item", "amount": 350, "currency": "usd", "description": "Example Product (Org-Roam Doc)", "discount_amounts": [ ], "discountable": true, "discounts": [ ], "invoice_item": "ii_1IZJF0IoFf3wvXpROwCaDRo0", "livemode": false, "metadata": { }, "period": { "end": 1616778874, "start": 1616778874 }, "plan": null, "price": { "id": "price_1IZHFSIoFf3wvXpRHwH3GRm8", "object": "price", "active": true, "billing_scheme": "per_unit", "created": 1616771214, "currency": "usd", "livemode": false, "lookup_key": null, "metadata": { }, "nickname": "Non-Recurring", "product": "prod_JBeWfGHNqBcy5B", "recurring": null, "tiers_mode": null, "transform_quantity": null, "type": "one_time", "unit_amount": 350, "unit_amount_decimal": "350" }, "proration": false, "quantity": 1, "subscription": null, "tax_amounts": [ ], "tax_rates": [ ], "type": "invoiceitem" } ], "has_more": false, "total_count": 1, "url": "/v1/invoices/in_1IZJF5IoFf3wvXpRu9PnTZIl/lines" }, "livemode": false, "metadata": { }, "next_payment_attempt": null, "number": "0EBAE885-0013", "on_behalf_of": null, "paid": true, "payment_intent": "pi_1IZJFcIoFf3wvXpR60FJfbvD", "payment_settings": { "payment_method_options": null, "payment_method_types": null }, "period_end": 1619449837, "period_start": 1616771437, "post_payment_credit_notes_amount": 0, "pre_payment_credit_notes_amount": 0, "receipt_number": null, "starting_balance": 0, "statement_descriptor": null, "status": "paid", "status_transitions": { "finalized_at": 1616778912, "marked_uncollectible_at": null, "paid_at": 1616778912, "voided_at": null }, "subscription": null, "subtotal": 350, "tax": null, "total": 350, "total_discount_amounts": [ ], "total_tax_amounts": [ ], "transfer_data": null, "webhooks_delivered_at": 1616778880 } #+end_example ** Error handling - Bad payment methods will fail to attach to the customer, stopping the purchase flow before we attempt to set up the invoice. - Failure to create an invoice leaves the invoice item on the customer. Because we do not re-use customers, this will not be invoiced and charged. - Invoice creation leaves the invoice in a draft state, from which it will not progress without an action being taken. - Payment failure results in the invoice being in a failed state, and it will not retry payment. - Customer can be cleaned up at any point, which will clean up all data pertaining to it (invoice items, draft / failed invoices). *** Attempting to attach a test payment method to a live customer #+HEADER: :var customer="cus_JihU7tb4eVdeda" #+HEADER: :var payment_method=payment-method #+begin_src shell stripe payment_methods attach "$payment_method" \ --live -p correl \ --customer="$customer" #+end_src #+RESULTS[e968f28eb95b5cc597fe402343d675f1fb5713de]: : { : "error": { : "code": "resource_missing", : "doc_url": "https://stripe.com/docs/error-codes/resource-missing", : "message": "No such paymentmethod: 'pm_1J5FujIoFf3wvXpRgemyYO93'; a similar object exists in test mode, but a live mode key was used to make this request.", : "param": "payment_method", : "type": "invalid_request_error" : } : } * Subscriptions Subscriptions require that a customer and payment method are already available. This means we'll need to have the client prepare a payment method, then pass that to us to be attached to the customer we create. We'll then be able to build the subscription, and the payment will be processed by Stripe. ** Create a payment method #+NAME: payment-method #+begin_src shell stripe payment_methods create \ --stripe-account=$account \ --type=card \ -d "card[number]"=4242424242424242 \ -d "card[exp_month]"=3 \ -d "card[exp_year]"=2022 \ -d "card[cvc]"=314 \ | jq -r '.id' #+end_src #+RESULTS[df876e7c97be477440f418bec23e78b376d86944]: payment-method : pm_1IZHILIoFf3wvXpR3oRjPoMx ** Attach the payment method to the customer #+HEADER: :var customer=customer #+HEADER: :var payment_method=payment-method #+begin_src shell stripe payment_methods attach "$payment_method" \ --stripe-account=$account \ --customer="$customer" #+end_src #+RESULTS[4290d9f7fe1f6c7362c61268ca3da026798046d0]: #+begin_example { "id": "pm_1IZHILIoFf3wvXpR3oRjPoMx", "object": "payment_method", "billing_details": { "address": { "city": null, "country": null, "line1": null, "line2": null, "postal_code": null, "state": null }, "email": null, "name": null, "phone": null }, "card": { "brand": "visa", "checks": { "address_line1_check": null, "address_postal_code_check": null, "cvc_check": "pass" }, "country": "US", "exp_month": 3, "exp_year": 2022, "fingerprint": "C5qD8oSCGvVCbtcH", "funding": "credit", "generated_from": null, "last4": "4242", "networks": { "available": [ "visa" ], "preferred": null }, "three_d_secure_usage": { "supported": true }, "wallet": null }, "created": 1616771394, "customer": "cus_JBeXOsWSmfXrDX", "livemode": false, "metadata": { }, "type": "card" } #+end_example ** Set the payment method as the default #+HEADER: :var customer=customer #+HEADER: :var payment_method=payment-method #+begin_src shell stripe customers update "$customer" \ --stripe-account=$account \ -d "invoice_settings[default_payment_method]=$payment_method" #+end_src #+RESULTS[be0aa9707c8abf6bb987623eb11acdc96f3931d6]: #+begin_example { "id": "cus_JBeXOsWSmfXrDX", "object": "customer", "address": null, "balance": 0, "created": 1616771128, "currency": "usd", "default_source": null, "delinquent": false, "description": "Org Roam Docs Customer", "discount": null, "email": "correl+stripe.roam.docs@gmail.com", "invoice_prefix": "0EBAE885", "invoice_settings": { "custom_fields": null, "default_payment_method": "pm_1IZHILIoFf3wvXpR3oRjPoMx", "footer": null }, "livemode": false, "metadata": { }, "name": "Correl Roush", "next_invoice_sequence": 2, "phone": null, "preferred_locales": [ ], "shipping": null, "tax_exempt": "none" } #+end_example ** Create subscription #+HEADER: :var customer=customer #+HEADER: :var price=price-recurring #+begin_src shell stripe subscriptions create \ --stripe-account=$account \ --customer="$customer" \ -d "items[0][price]"=$price #+end_src #+RESULTS[a3e938342dd705eb0de47989fb744ef0756905ea]: #+begin_example { "id": "sub_JBec2LL8kvDiPi", "object": "subscription", "application_fee_percent": null, "billing_cycle_anchor": 1616771437, "billing_thresholds": null, "cancel_at": null, "cancel_at_period_end": false, "canceled_at": null, "collection_method": "charge_automatically", "created": 1616771437, "current_period_end": 1619449837, "current_period_start": 1616771437, "customer": "cus_JBeXOsWSmfXrDX", "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_JBecQCXkjKpi2j", "object": "subscription_item", "billing_thresholds": null, "created": 1616771438, "metadata": { }, "plan": { "id": "price_1IZHFZIoFf3wvXpRtHW5EKFo", "object": "plan", "active": true, "aggregate_usage": null, "amount": 999, "amount_decimal": "999", "billing_scheme": "per_unit", "created": 1616771221, "currency": "usd", "interval": "month", "interval_count": 1, "livemode": false, "metadata": { }, "nickname": "Recurring", "product": "prod_JBeWfGHNqBcy5B", "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "price": { "id": "price_1IZHFZIoFf3wvXpRtHW5EKFo", "object": "price", "active": true, "billing_scheme": "per_unit", "created": 1616771221, "currency": "usd", "livemode": false, "lookup_key": null, "metadata": { }, "nickname": "Recurring", "product": "prod_JBeWfGHNqBcy5B", "recurring": { "aggregate_usage": null, "interval": "month", "interval_count": 1, "trial_period_days": null, "usage_type": "licensed" }, "tiers_mode": null, "transform_quantity": null, "type": "recurring", "unit_amount": 999, "unit_amount_decimal": "999" }, "quantity": 1, "subscription": "sub_JBec2LL8kvDiPi", "tax_rates": [ ] } ], "has_more": false, "total_count": 1, "url": "/v1/subscription_items?subscription=sub_JBec2LL8kvDiPi" }, "latest_invoice": "in_1IZHJ3IoFf3wvXpR6u3Yylyu", "livemode": false, "metadata": { }, "next_pending_invoice_item_invoice": null, "pause_collection": null, "pending_invoice_item_interval": null, "pending_setup_intent": null, "pending_update": null, "plan": { "id": "price_1IZHFZIoFf3wvXpRtHW5EKFo", "object": "plan", "active": true, "aggregate_usage": null, "amount": 999, "amount_decimal": "999", "billing_scheme": "per_unit", "created": 1616771221, "currency": "usd", "interval": "month", "interval_count": 1, "livemode": false, "metadata": { }, "nickname": "Recurring", "product": "prod_JBeWfGHNqBcy5B", "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "quantity": 1, "schedule": null, "start_date": 1616771437, "status": "active", "transfer_data": null, "trial_end": null, "trial_start": null } #+end_example ** Failure cases Stripe provides a [[https://stripe.com/docs/testing#cards-responses][set of credit card numbers]] for eliciting various responses from their API. *** Charge failure #+HEADER: :var customer=customer #+HEADER: :var price=price-recurring #+begin_src shell payment_method=$(stripe payment_methods create \ --stripe-account=$account \ --type=card \ -d "card[number]"=4000000000000341 \ -d "card[exp_month]"=3 \ -d "card[exp_year]"=2022 \ -d "card[cvc]"=314 \ | jq -r '.id') stripe payment_methods attach "$payment_method" \ --stripe-account=$account \ --customer="$customer" \ >/dev/null # stripe customers update "$customer" \ # --stripe-account=$account \ # -d "invoice_settings[default_payment_method]=$payment_method" >/dev/null stripe subscriptions create \ --stripe-account=$account \ --customer="$customer" \ -d "default_payment_method"=$payment_method \ -d "items[0][price]"=$price #+end_src #+RESULTS[7065d76c5fb4d771b666fc2b39b57cc589255de7]: #+begin_example { "id": "sub_JgoaKuIlHIVW7l", "object": "subscription", "application_fee_percent": null, "automatic_tax": { "enabled": false }, "billing_cycle_anchor": 1623958380, "billing_thresholds": null, "cancel_at": null, "cancel_at_period_end": false, "canceled_at": null, "collection_method": "charge_automatically", "created": 1623958380, "current_period_end": 1626550380, "current_period_start": 1623958380, "customer": "cus_JBeXOsWSmfXrDX", "days_until_due": null, "default_payment_method": "pm_1J3QxSIoFf3wvXpRCCwKw9Cj", "default_source": null, "default_tax_rates": [ ], "discount": null, "ended_at": null, "items": { "object": "list", "data": [ { "id": "si_Jgoa4zrlQvwRbA", "object": "subscription_item", "billing_thresholds": null, "created": 1623958381, "metadata": { }, "plan": { "id": "price_1IZHFZIoFf3wvXpRtHW5EKFo", "object": "plan", "active": true, "aggregate_usage": null, "amount": 999, "amount_decimal": "999", "billing_scheme": "per_unit", "created": 1616771221, "currency": "usd", "interval": "month", "interval_count": 1, "livemode": false, "metadata": { }, "nickname": "Recurring", "product": "prod_JBeWfGHNqBcy5B", "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "price": { "id": "price_1IZHFZIoFf3wvXpRtHW5EKFo", "object": "price", "active": true, "billing_scheme": "per_unit", "created": 1616771221, "currency": "usd", "livemode": false, "lookup_key": null, "metadata": { }, "nickname": "Recurring", "product": "prod_JBeWfGHNqBcy5B", "recurring": { "aggregate_usage": null, "interval": "month", "interval_count": 1, "trial_period_days": null, "usage_type": "licensed" }, "tiers_mode": null, "transform_quantity": null, "type": "recurring", "unit_amount": 999, "unit_amount_decimal": "999" }, "quantity": 1, "subscription": "sub_JgoaKuIlHIVW7l", "tax_rates": [ ] } ], "has_more": false, "total_count": 1, "url": "/v1/subscription_items?subscription=sub_JgoaKuIlHIVW7l" }, "latest_invoice": "in_1J3QxUIoFf3wvXpRyWAuAxGq", "livemode": false, "metadata": { }, "next_pending_invoice_item_invoice": null, "pause_collection": null, "pending_invoice_item_interval": null, "pending_setup_intent": null, "pending_update": null, "plan": { "id": "price_1IZHFZIoFf3wvXpRtHW5EKFo", "object": "plan", "active": true, "aggregate_usage": null, "amount": 999, "amount_decimal": "999", "billing_scheme": "per_unit", "created": 1616771221, "currency": "usd", "interval": "month", "interval_count": 1, "livemode": false, "metadata": { }, "nickname": "Recurring", "product": "prod_JBeWfGHNqBcy5B", "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "quantity": 1, "schedule": null, "start_date": 1623958380, "status": "incomplete", "transfer_data": null, "trial_end": null, "trial_start": null } #+end_example * A Unified Purchasing Experience Both [[One-Time Invoicing (Payment method first)]] and [[Subscriptions]] require a payment intent be prepared by the client and to be provided to our backend along with customer information.