Stripe Connect: paying users
Notes
You can use Stripe Connect a few different ways
 
 
 
 
Special note about Ireland
Stripe charges VAT on their fee
 
VAT is not added in other EU countries
 
https://dashboard.stripe.com/test/payments/pi_1ICXr4DZMz26SgvEgoRoKHm8
 
 

3 options for charge type:

https://stripe.com/docs/connect/charges#types

Option 1: Direct charges

stripe.PaymentIntent.create(
  amount=1099,
  currency='usd',
  payment_method_types=['card'],
  
Mark's comments
Pro
  • Simple
Cons
  • Recommended for Standard, generally not recommended for Express accounts
  • Charges created directly on the connected account are reported only on that account; they aren’t shown in your platform’s Dashboard, exports, or other reporting, although you can always retrieve this information with a call to the API.
  • It's probably possible for the seller to leave Awesound, taking their subscribers with them. This could be a feature or a bug, depending on our preference.
Notes:
  • To charge a card, the Connect account must have service_type 'full' not 'recipient'.
Conclusion: we probably won't do Direct charges.
 
https://stripe.com/docs/connect/direct-charges
  • Recommended for Standard accounts.
  • Money goes to seller first, so clearly Awesound's revenue is just the application_fee
  • The connected account is responsible for Stripe fees, refunds, and chargebacks.
  • refund_application_feeis false by default – we can create a refund, but keep our fee unless we specify otherwise.

Option 2: Destination charges

 
Mark's comments:
  • Probably the simplest way to get started using Connect for payments with Express accounts
  • Doesn't support paying 2+ people for one purchase, e.g., the author and their agent (doing revenue share), or the author + voice actor doing revenue share.
  • TBD if it's possible for an Irishman to sell a book in USD and get paid via a Destination charge in real-time.
    Stripe's points:
    • Destination charges are recommended for Express or Custom accounts. Destination charges are created on the platform, but as part of the charge operation, funds are transferred to the connected account specified in the transfer_data[destination] parameter of the charge.
    • The platform is responsible for the cost of the Stripe fees, refunds, and chargebacks.
    • Only one recipient can be set. The recipient is set at the same time as the payment
      • # Option 1: specify the application_fee_amount
        # Option 1: specificy the application_fee_amount
        payment_intent = stripe.PaymentIntent.create(
          payment_method_types=['card'],
          amount=1000,
          currency='usd',
          application_fee_amount=123,
          transfer_data={
            'destination': '{{CONNECTED_STRIPE_ACCOUNT_ID}}',
          }
        )
        # Option 2: specify the transfer_data['amount']
        # Option 2: specify the transfer_data['amount']
        payment_intent = stripe.PaymentIntent.create(
          payment_method_types=['card'],
          amount=1000,
          currency='usd',
          transfer_data={
            'amount': 877,
            'destination': '{{CONNECTED_STRIPE_ACCOUNT_ID}}',
          }
        )
     

    on_behalf_of

    This only applies to Destination charges

    Option 3: Separate charges and transfers are supported if both your platform and the connected account are in the same region (for example, both in Australia).

     
    💡
    Separate charges and transfers are supported if both your platform and the connected account are in the same region (for example, both in Australia).
    • Con: For our US Stripe account, only US sellers could do this clever split-revenue thing with later payouts
    • TBD: All non-US sellers need to have a tos_acceptance='service' defined up-front when creating the Stripe Account; otherwise we can't transfer them money.
     
     

    Service agreement type

    • Must be set when we create Stripe Connect Account
    • full is required to request card_payments
    • service is required for cross-border payments
    TBD can an Irish Account Owner sell an audiobook in USD using a destination charge?

    Questions for Stripe

    What is the effect of including the transfer_group argument?
    Can I create separate charges and transfers for international folks, if I just don't include 'transfer_group' argument?
     

    Four facts that lead me to think non-US sellers might need two Stripe accounts

    • "Separate charges and transfers are supported if both your platform and the connected account are in the same region (for example, both in Australia)." OK, so I can't use stripe.Transfer.create() with a transfer_group parameter for the UK person.
      • Let's test this
        // from https://stripe.com/docs/connect/charges-transfers
        // Create a PaymentIntent:
        const paymentIntent = await stripe.paymentIntents.create({
          amount: 10000,
          currency: 'usd',
          payment_method_types: ['card'],
          transfer_group: 'testingCrossBorder',
        });
        
        // Create a Transfer to the connected account (US):
        const transfer = await stripe.transfers.create({
          amount: 7000,
          currency: 'usd',
          destination: '{{CONNECTED_STRIPE_ACCOUNT_ID}}',
          transfer_group: 'testingCrossBorder',
        });
        
        // Create a second Transfer to another connected account (non-US):
        const secondTransfer = await stripe.transfers.create({
          amount: 2000,
          currency: 'usd',
          destination: '{{OTHER_CONNECTED_STRIPE_ACCOUNT_ID}}',
          transfer_group: 'testingCrossBorder',
        });
     
    transfer_group argument is optional when calling stripe.Transfer.create()
     
     

    on_behalf_of

     

    Subscriptions

    Hah. So I had it all figured out for one-time payments, and it turns out subscriptions are different.
     
    • Option 1: Create the subscription authorised as the connected account.
      • Pro: the fees are beautifully simple our end: we just see the €0.50 we take.
      • Minor Con: We have to listen separately to webhooks for the connected account
      • Con: the price needs to be pre-defined in the connected account, which complicates the update_playlist_price code, slightly.
      • Con: the Stripe account must be fully set up before they can make their first sale (I think)
    • Option 2: Create the subscription on our account, with transfer_data
      • Con: Won't work for non-US accounts
      •  
         

    Code example: stripe.Subscription.create()

     
    # test mode
    subscription = stripe.Subscription.create(
      customer="cus_IpB71tktXVgTGq", # "cus_4fdAW5ftNQow1a",
      items=[
        {
          "price": "price_1IKOvyDZMz26SgvEXaK2dGIj", # velotest  "price_H1y51TElsOZjG",
        },
      ],
      expand=["latest_invoice.payment_intent"],
      application_fee_percent=10,
      transfer_data={
        "destination": "acct_1IBRVODFBHb3LtaI", // account based in Ireland with 'full' service agreement
      },
    )
    stripe.error.InvalidRequestError: Request req_OWYmICqJ6BCiYt: Cannot create a destination charge for connected accounts in IE because funds would be settled on the platform and the connected account is outside the platform's region. You may use the on_behalf_of parameter to have the charge settle in the connected account's country. For more information on on_behalf_of, see https://stripe.com/docs/connect/charges-transfers#on-behalf-of. If using capabilities (e.g. card_payments, transfers), note that they affect settlement as well. For more information on capabilities, see https://stripe.com/docs/connect/account-capabilities. If you still need assistance, please contact us via https://support.stripe.com/contact.
     
    OK, so we add the on_behalf_of parameter it suggests… but for Subscription.create(), that's not recognised:
    stripe.api_key = 'sk_test_517j8p4DZMz26SgvEvpTETIDGMAtuImAx73aryLqy6HJxvCP1jBksYErbOYHkIdmE5Xeyzx511kGn7eQmTQpLkHSI00VvUKjyr6'
    
    subscription = stripe.Subscription.create(
      customer="cus_IpB71tktXVgTGq", # "cus_4fdAW5ftNQow1a",
      items=[
        {
          "price": "price_1IKOvyDZMz26SgvEXaK2dGIj", # velotest  "price_H1y51TElsOZjG",
        },
      ],
    stripe.error.InvalidRequestError: Request req_h5LzCq38HUlu8U: Received unknown parameter: on_behalf_of
     

    Code example: Use Checkout to create the subscription

    As seen in the above example, transfer_data is used for destination charges, but that doesn't work for international accounts, it seems. Alas, we'll use direct charges, so. This complicates webhooks (need to listen for webhooks on connected account) and complicates other things (cu
     
    transfer_data and on_behalf_of are not possible when creating a subscription via Stripe Checkout https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-subscription_data
     

    Direct charges: To make API calls on behalf of a connected account, add a second argument, object

     
    var stripe = Stripe("{{PLATFORM_PUBLISHABLE_KEY }}", { stripeAccount: "{{CONNECTED_STRIPE_ACCOUNT_ID}}"});
    // buyButton.jsx
    
        if (playlist.interval != "onnce") {
          // For subscriptions, use direct charges to avoid currency conversion.
          stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY, {
            stripeAccount: playlist.connectStripeAcId, // "acct_1IBRVODFBHb3LtaI",
          });
        } else {
          stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY);
        }
    
    
    // create-checkout-session.js
        if (checkout_mode == "subscription" && connectStripeAcId) {
          const stripeSession = await stripe.checkout.sessions.create(postData, {
            stripeAccount: connectStripeAcId,
          });
          res.json(stripeSession);
        } else {
          const stripeSession = await stripe.checkout.sessions.create(postData);
          res.json(stripeSession);
        }