v0.3.0
NewLatestAdded
- Stripe bank transfers (opt-in:
bankTransfer: { stripe: { enabled: true } }). Bank-transfer orders in GBP get a Stripe virtual account (sort code12-34-56and account number) and Stripe's reference instead of your account details, and settle by themselves: Stripe matches the incoming Faster Payment or BACS credit,payment_intent.succeededreaches/checkout-guard/stripe-webhook, and the order's Authorized bank-transfer payment is settled under a row lock in the order's own channel, so the order moves toPaymentSettledas if an admin had clicked Mark received. The details travel inmetadata.publicunder the existing field names (accountName,sortCode,accountNumber,reference,payBy,instructions), plusprovider: 'stripe'andhostedInstructionsUrl, so storefronts and e-mails need no change to show them; the PaymentIntent id stays in the admin-only metadata. The Stripe secret key is the channel's Stripe payment methodapiKey, as for holds, preferring the method that belongs to that channel alone (the default channel also sees other channels' methods); the choice is logged once per channel. - Whose Stripe customer. Stripe applies a customer's cash balance to a new PaymentIntent at once, so an existing Stripe customer is reused only when the checkout is signed in as the order's (registered) customer, and only the one this plugin created for that customer and user (
cg_vendure_customer_idandcg_vendure_user_idmetadata). Every other checkout, guests included, gets a Stripe customer of its own for that order, so typing someone's e-mail address can never spend their balance; a balance applied to such an order anyway is refused (static details, alert). - Sweep fallback for missed webhooks: every ten minutes (worker, under the bank-transfer sweep lock, never longer than three minutes a run) the plugin asks Stripe about each open Stripe transfer of an order still awaiting payment, newest first and at most once per ten minutes per payment, and settles those that have succeeded. A part-paid transfer stays open, the log shows the
amount_remaining, andmetadata.public.amountMinoris lowered to what is still due. Before the expiry sweep expires a Stripe transfer it checks with Stripe: a paid one is settled instead, one Stripe cannot be asked about is left for the next pass (alerted once it is a day overdue), and one Stripe says is not on that account is expired as usual. - Cancelling an unpaid Stripe transfer (expiry, Checkout Guard → Bank transfers, Vendure's Cancel payment, and Vendure's Cancel order, which leaves payments alone, so the plugin cancels the open transfer of a cancelled order itself) cancels its PaymentIntent at Stripe, best effort. A part payment the cancel returns to the customer's cash balance is alerted on every path.
- Settling by hand (Mark received, Vendure's Settle payment) cancels the PaymentIntent too, unless Stripe has the money, in which case it counts as Stripe's settlement; a later Stripe receipt for a payment settled by hand is alerted as a possible double payment.
customer_cash_balance_transaction.createdevents: money left in the cash balance of a Stripe customer this plugin created (a transfer after the order was cancelled or expired, an overpayment) is alerted, once per amount, for a refund.currencies(default['GBP']),euCountryfor EUR (eu_bank_transfer, IBAN and BIC;BE,DE,FR,IEorNL) andinstructionsoptions;providerandpaymentIntentIdon the Bank transfers list rows; ops eventsbank.stripe_fallback,bank.stripe_cancel_failed,bank.stripe_unsettled,bank.stripe_partially_fundedandbank.stripe_check_failed, delivered on every tier.
Notes
- Checkout never fails because of Stripe: with no key, an API error, no answer within 15 seconds, a refused currency or a reply without instructions, the order gets the payment method's own details as before, a warning is logged and
bank.stripe_fallbackis raised. When the PaymentIntent request itself got no answer, the plugin looks for that order's intent 30 seconds and 5 minutes later and cancels it if no payment carries it. - A second
addPaymentToOrderfor the same checkout (a double click) is refused while the first one is creating the intent: the handler locks the order row first. Two payments carrying one intent are never both settled. - The Stripe calls (normally two, at most 15 seconds) run inside Vendure's
addPaymentToOrdertransaction, as any payment handler's do. - Hosts that enable it must set
StripePlugin.init({ skipPaymentIntentsWithoutExpectedMetadata: true }): these PaymentIntents carry onlycg_*metadata, and Vendure's own Stripe webhook otherwise throws on them and Stripe retries it for days. Keep bank transfers out of the card flow (excluded_payment_method_types: ['customer_balance']inpaymentIntentCreateParams), and addpayment_intent.succeeded,payment_intent.partially_fundedandcustomer_cash_balance_transaction.createdto every endpoint that targets/checkout-guard/stripe-webhook. In this mode an event its channel's secret cannot verify is checked against the other configured secrets (one URL, several Stripe accounts), and while premium is locked the hold module acknowledges events about payments that are not holds. - Off by default. Without
bankTransfer.stripe.enabledthe handler, the sweeps and the webhook behave exactly as in 0.2.9, the new ten-minute job does nothing, and every webhook event withoutcg_bank_transfermetadata is handled as before.