diff --git a/.env.example b/.env.example index 4bd2bf03..9b25b777 100644 --- a/.env.example +++ b/.env.example @@ -20,7 +20,7 @@ STRIPE_ACCOUNT_COUNTRY=US # Make sure to check the docs: https://stripe.com/docs/sources # Only used for servers that don't have a separate config/settings file. # For Node.js see the server/node/config.js file! -PAYMENT_METHODS="alipay, bancontact, card, eps, ideal, giropay, multibanco, p24, sofort, wechat" +PAYMENT_METHODS="alipay, bancontact, card, eps, ideal, giropay, multibanco, p24, sofort, wechat, au_becs_debit" # Optional ngrok configuration for development (if you have a paid ngrok account). NGROK_SUBDOMAIN= diff --git a/.gitignore b/.gitignore index 3f120fe8..12ac08b4 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ typings/ # serverless serverless/ +.vscode/launch.json diff --git a/public/index.html b/public/index.html index cec5cee4..11a940ba 100644 --- a/public/index.html +++ b/public/index.html @@ -143,6 +143,10 @@

Payment Information

+
  • + + +
  • @@ -182,11 +186,25 @@

    Payment Information

    Click the button below to generate a QR code for WeChat.

    +
    +
    + +
    +

    By providing your bank account details and confirming this payment, you agree to this Direct Debit Request and + the Direct Debit Request service agreement, and authorise Stripe Payments Australia Pty Ltd ACN 160 180 343 Direct Debit User ID + number 507156 (“Stripe”) to debit your account through the Bulk Electronic Clearing System (BECS) on behalf of Stripe Payments Demo + (the “Merchant”) for any amounts separately communicated to you by the Merchant. You certify that you are either an account + holder or an authorised signatory on the account listed above.

    +
    +
    diff --git a/public/javascripts/payments.js b/public/javascripts/payments.js index 4b830cd1..607b7a1f 100644 --- a/public/javascripts/payments.js +++ b/public/javascripts/payments.js @@ -16,6 +16,7 @@ // Retrieve the configuration for the store. const config = await store.getConfig(); + let activeCurrency = config.currency; // Create references to the main form and its submit button. const form = document.getElementById('payment-form'); @@ -131,6 +132,33 @@ // Mount the iDEAL Bank Element on the page. idealBank.mount('#ideal-bank-element'); + /** + * Add a BECS element that matches the look-and-feel of the app. + * + * This allows you to collect australian bank account details + */ + + const becsBank = elements.create('auBankAccount', {style}); + + // Mount the BECS element on the page. + becsBank.mount('#becs-bank-element'); + + // Monitor change events on the BECS Element to display any errors. + becsBank.on('change', ({error, bankName}) => { + const becsBankErrors = document.getElementById('becs-errors'); + if (error) { + becsBankErrors.textContent = error.message; + becsBankErrors.classList.add('visible'); + } else { + becsBankErrors.classList.remove('visible'); + if (bankName) { + updateButtonLabel('au_becs_debit', bankName); + } + } + // Re-enable the Pay button. + submitButton.disabled = false; + }); + /** * Implement a Stripe Payment Request Button Element. * @@ -215,7 +243,7 @@ }); const amount = store.formatPrice( response.paymentIntent.amount, - config.currency + activeCurrency ); updateSubmitButtonPayText(`Pay ${amount}`); }); @@ -283,6 +311,20 @@ submitButton.disabled = true; submitButton.textContent = 'Processing…'; + // Update Payment Intent if currency is different to default + if (config.currency !== activeCurrency) { + const response = await store.updatePaymentIntentCurrency( + paymentIntent.id, + activeCurrency, + [payment] + ); + + if (response.error) { + handleError(response); + return; + } + } + if (payment === 'card') { // Let Stripe.js handle the confirmation of the PaymentIntent with the card Element. const response = await stripe.confirmCardPayment( @@ -321,8 +363,8 @@ payment_method: { billing_details: { name, - email - } + email, + }, }, return_url: window.location.href, } @@ -337,8 +379,8 @@ ideal: idealBank, billing_details: { name, - email - } + email, + }, }, return_url: window.location.href, } @@ -351,7 +393,7 @@ payment_method: { billing_details: { name, - } + }, }, return_url: window.location.href, } @@ -364,7 +406,7 @@ payment_method: { billing_details: { name, - } + }, }, return_url: window.location.href, } @@ -377,7 +419,7 @@ payment_method: { billing_details: { name, - } + }, }, return_url: window.location.href, } @@ -390,12 +432,26 @@ payment_method: { billing_details: { name, - } + }, }, return_url: window.location.href, } ); handlePayment(response); + } else if (payment == 'au_becs_debit') { + const response = await stripe.confirmAuBecsDebitPayment( + paymentIntent.client_secret, + { + payment_method: { + au_becs_debit: becsBank, + billing_details: { + name, + email, + }, + }, + } + ); + handlePayment(response); } else { // Prepare all the Stripe source common data. const sourceData = { @@ -434,6 +490,7 @@ const {source} = await stripe.createSource(sourceData); handleSourceActivation(source); } + }); // Handle new PaymentIntent result @@ -472,7 +529,8 @@ } else if (paymentIntent.status === 'requires_payment_method') { // Failure. Requires new PaymentMethod, show last payment error message. mainElement.classList.remove('processing'); - confirmationElement.querySelector('.error-message').innerText = paymentIntent.last_payment_error || 'Payment failed'; + confirmationElement.querySelector('.error-message').innerText = + paymentIntent.last_payment_error || 'Payment failed'; mainElement.classList.add('error'); } else { // Payment has failed. @@ -483,6 +541,27 @@ } }; + const handleError = (updateResponse) => { + // handle any error + const {paymentIntent, error} = updateResponse; + + const mainElement = document.getElementById('main'); + const confirmationElement = document.getElementById('confirmation'); + + if (error && error.type === 'validation_error') { + mainElement.classList.remove('processing'); + mainElement.classList.remove('receiver'); + submitButton.disabled = false; + submitButton.textContent = submitButtonPayText; + } else if (error) { + mainElement.classList.remove('processing'); + mainElement.classList.remove('receiver'); + confirmationElement.querySelector('.error-message').innerText = + error.message; + mainElement.classList.add('error'); + } + }; + // Handle activation of payment sources not yet supported by PaymentIntents const handleSourceActivation = (source) => { const mainElement = document.getElementById('main'); @@ -505,7 +584,7 @@ form.querySelector('.payment-info.wechat p').style.display = 'none'; let amount = store.formatPrice( store.getPaymentTotal(), - config.currency + activeCurrency ); updateSubmitButtonPayText( `Scan this QR code on WeChat to pay ${amount}` @@ -530,7 +609,7 @@ const receiverInfo = confirmationElement.querySelector( '.receiver .info' ); - let amount = store.formatPrice(source.amount, config.currency); + let amount = store.formatPrice(source.amount, activeCurrency); switch (source.type) { case 'ach_credit_transfer': // Display the ACH Bank Transfer information to the user. @@ -592,9 +671,12 @@ */ const paymentIntentTerminalState = ({status, last_payment_error}) => { const endStates = ['succeeded', 'processing', 'canceled']; - const hasError = typeof last_payment_error !== "undefined"; + const hasError = typeof last_payment_error !== 'undefined'; - return endStates.includes(status) || (status === 'requires_payment_method' && hasError); + return ( + endStates.includes(status) || + (status === 'requires_payment_method' && hasError) + ); }; /** @@ -612,15 +694,18 @@ start = null ) => { start = start ? start : Date.now(); + const endStates = [ + 'succeeded', + 'processing', + 'canceled', + 'requires_payment_method', + ]; // Retrieve the PaymentIntent status from our server. const rawResponse = await fetch(`payment_intents/${paymentIntent}/status`); const response = await rawResponse.json(); const isTerminalState = paymentIntentTerminalState(response.paymentIntent); - if ( - !isTerminalState && - Date.now() < start + timeout - ) { + if (!isTerminalState && Date.now() < start + timeout) { // Not done yet. Let's wait and check again. setTimeout( pollPaymentIntentStatus, @@ -643,7 +728,10 @@ const mainElement = document.getElementById('main'); if (url.searchParams.get('payment_intent')) { - if (url.searchParams.get('source') && url.searchParams.get('client_secret')) { + if ( + url.searchParams.get('source') && + url.searchParams.get('client_secret') + ) { mainElement.classList.add('checkout', 'success', 'processing'); } // Poll the PaymentIntent status. @@ -770,11 +858,17 @@ 'usd', ], }, + au_becs_debit: { + name: 'BECS Direct Debit', + flow: 'none', + countries: ['AU'], + currencies: ['aud'], + }, }; // Update the main button to reflect the payment method being selected. const updateButtonLabel = (paymentMethod, bankName) => { - let amount = store.formatPrice(store.getPaymentTotal(), config.currency); + let amount = store.formatPrice(store.getPaymentTotal(), activeCurrency); let name = paymentMethods[paymentMethod].name; let label = `Pay ${amount}`; if (paymentMethod !== 'card') { @@ -783,9 +877,10 @@ if (paymentMethod === 'wechat') { label = `Generate QR code to pay ${amount} with ${name}`; } - if (paymentMethod === 'sepa_debit' && bankName) { + if (['sepa_debit', 'au_becs_debit'].includes(paymentMethod) && bankName) { label = `Debit ${amount} from ${bankName}`; } + updateSubmitButtonPayText(label); }; @@ -794,6 +889,13 @@ selector.querySelector(`option[value=${country}]`).selected = 'selected'; selector.className = `field ${country.toLowerCase()}`; + //update currency if there's a currency for that country + if (country === 'AU') { + activeCurrency = 'aud'; + } else { + activeCurrency = config.currency; + } + // Trigger the methods to show relevant fields and payment methods on page load. showRelevantFormFields(); showRelevantPaymentMethods(); @@ -852,6 +954,7 @@ if (!country) { country = form.querySelector('select[name=country] option:checked').value; } + const paymentInputs = form.querySelectorAll('input[name=payment]'); for (let i = 0; i < paymentInputs.length; i++) { let input = paymentInputs[i]; @@ -860,7 +963,7 @@ input.value === 'card' || (config.paymentMethods.includes(input.value) && paymentMethods[input.value].countries.includes(country) && - paymentMethods[input.value].currencies.includes(config.currency)) + paymentMethods[input.value].currencies.includes(activeCurrency)) ); } @@ -878,6 +981,9 @@ form.querySelector('.payment-info.sepa_debit').classList.remove('visible'); form.querySelector('.payment-info.wechat').classList.remove('visible'); form.querySelector('.payment-info.redirect').classList.remove('visible'); + form + .querySelector('.payment-info.au_becs_debit') + .classList.remove('visible'); updateButtonLabel(paymentInputs[0].value); }; @@ -904,6 +1010,9 @@ form .querySelector('.payment-info.wechat') .classList.toggle('visible', payment === 'wechat'); + form + .querySelector('.payment-info.au_becs_debit') + .classList.toggle('visible', payment === 'au_becs_debit'); form .querySelector('.payment-info.redirect') .classList.toggle('visible', flow === 'redirect'); diff --git a/public/javascripts/store.js b/public/javascripts/store.js index 6f6f6a78..26c99599 100644 --- a/public/javascripts/store.js +++ b/public/javascripts/store.js @@ -140,6 +140,35 @@ class Store { } } + // Update the PaymentIntent with the the currency and payment value. + async updatePaymentIntentCurrency( + paymentIntent, + currency, + payment_methods, + ) { + try { + const response = await fetch( + `/payment_intents/${paymentIntent}/update_currency`, + { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + currency, + payment_methods, + }), + } + ); + const data = await response.json(); + if (data.error) { + return {error: data.error}; + } else { + return data; + } + } catch (err) { + return {error: err.message}; + } + } + // Format a price (assuming a two-decimal currency like EUR or USD for simplicity). formatPrice(amount, currency) { let price = (amount / 100).toFixed(2); diff --git a/server/go/app.go b/server/go/app.go index f3a7733c..bfdb0213 100644 --- a/server/go/app.go +++ b/server/go/app.go @@ -143,6 +143,31 @@ func buildEcho(publicDirectory string) *echo.Echo { LastPaymentError string `json:"last_payment_error,omitempty"` } + type PaymentIntentsStatus struct { + PaymentIntent PaymentIntentsStatusData `json:"paymentIntent"` + } + e.POST("/payment_intents/:id/update_currency", func(c echo.Context) error { + r := new(payments.IntentCurrencyPaymentMethodsChangeRequest) + err := c.Bind(r) + if err != nil { + return err + } + + pi, err := payments.UpdateCurrencyPaymentMethod(c.Param("id"), r) + if err != nil { + return err + } + + return c.JSON(http.StatusOK, map[string]*stripe.PaymentIntent{ + "paymentIntent": pi, + }) + }) + + type PaymentIntentsStatusData struct { + Status string `json:"status"` + LastPaymentError string `json:"last_payment_error,omitempty"` + } + type PaymentIntentsStatus struct { PaymentIntent PaymentIntentsStatusData `json:"paymentIntent"` } diff --git a/server/go/payments/payments.go b/server/go/payments/payments.go index c720dae4..8f934def 100644 --- a/server/go/payments/payments.go +++ b/server/go/payments/payments.go @@ -20,16 +20,25 @@ type IntentShippingChangeRequest struct { ShippingOption inventory.ShippingOption `json:"shippingOption"` } +type IntentCurrencyPaymentMethodsChangeRequest struct { + Currency string `json:"currency"` + PaymentMethods []string `json:"payment_methods"` +} + func CreateIntent(r *IntentCreationRequest) (*stripe.PaymentIntent, error) { amount, err := inventory.CalculatePaymentAmount(r.Items) if err != nil { return nil, fmt.Errorf("payments: error computing payment amount: %v", err) } + // build initial payment methods which should exclude currency specific ones + initPaymentMethods := config.PaymentMethods(); + removeVal(initPaymentMethods, "au_becs_debit") + params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(amount), Currency: stripe.String(r.Currency), - PaymentMethodTypes: stripe.StringSlice(config.PaymentMethods()), + PaymentMethodTypes: stripe.StringSlice(initPaymentMethods), } pi, err := paymentintent.New(params) if err != nil { @@ -39,6 +48,18 @@ func CreateIntent(r *IntentCreationRequest) (*stripe.PaymentIntent, error) { return pi, nil } +// helper function to remove a value from a slice +func removeVal(slice []string, value string) ([]string) { + for i, other := range slice { + if other == value { + return append(slice[:i], slice[i+1:]...) + } + } + + return slice +} + + func RetrieveIntent(paymentIntent string) (*stripe.PaymentIntent, error) { pi, err := paymentintent.Get(paymentIntent, nil) if err != nil { @@ -100,3 +121,19 @@ func UpdateShipping(paymentIntent string, r *IntentShippingChangeRequest) (*stri return pi, nil } + +func UpdateCurrencyPaymentMethod(paymentIntent string, r *IntentCurrencyPaymentMethodsChangeRequest) (*stripe.PaymentIntent, error) { + currency := r.Currency + paymentMethods := r.PaymentMethods + + params := &stripe.PaymentIntentParams{ + Currency: stripe.String(currency), + PaymentMethodTypes: stripe.StringSlice(paymentMethods), + } + pi, err := paymentintent.Update(paymentIntent, params) + if err != nil { + return nil, fmt.Errorf("payments: error updating payment intent: %v", err) + } + + return pi, nil +} diff --git a/server/java/src/main/java/app/Application.java b/server/java/src/main/java/app/Application.java index 5907238e..600d1ec6 100644 --- a/server/java/src/main/java/app/Application.java +++ b/server/java/src/main/java/app/Application.java @@ -21,6 +21,7 @@ public static void main(String[] args) { get("/payment_intents/:id/status", PaymentController.getPaymentIntent); post("/payment_intents", PaymentController.createPaymentIntent); post("/payment_intents/:id/shipping_change", PaymentController.updatePaymentIntent); + post("/payment_intents/:id/update_currency", PaymentController.updatePaymentIntentCurrency); post("/webhook", FulfillmentController.webhookReceived); } -} \ No newline at end of file +} diff --git a/server/java/src/main/java/app/payment/Payment.java b/server/java/src/main/java/app/payment/Payment.java index 5376eb7a..236686dc 100644 --- a/server/java/src/main/java/app/payment/Payment.java +++ b/server/java/src/main/java/app/payment/Payment.java @@ -16,7 +16,10 @@ public Payment(Basket basket) throws StripeException { Map paymentintentParams = new HashMap(); paymentintentParams.put("amount", Inventory.calculatePaymentAmount(basket.items)); paymentintentParams.put("currency", basket.currency); + + //build initial payment methods which should exclude currency specific ones List payment_method_types = new ArrayList(); + payment_method_types.remove("au_becs_debit"); payment_method_types = Arrays.asList(Optional.ofNullable(System.getenv("PAYMENT_METHODS")).orElse("card").split("\\s*,\\s*")); paymentintentParams.put( "payment_method_types", @@ -45,6 +48,18 @@ public static PaymentIntent updateShippingCost(String paymentIntentId, Basket ba } + public static PaymentIntent updateCurrencyPaymentMethods(String paymentIntentId, String currency, String[] paymentMethods) throws StripeException { + + PaymentIntent paymentIntent = PaymentIntent.retrieve(paymentIntentId); + + Map paymentintentParams = new HashMap(); + paymentintentParams.put("currency", currency); + paymentintentParams.put("payment_method_types", paymentMethods); + + return paymentIntent.update(paymentintentParams); + + } + public static PaymentIntent getPaymentIntent(String id) throws StripeException { Stripe.apiKey = System.getenv("STRIPE_SECRET_KEY"); diff --git a/server/java/src/main/java/app/payment/PaymentController.java b/server/java/src/main/java/app/payment/PaymentController.java index 04184b88..c926e14c 100644 --- a/server/java/src/main/java/app/payment/PaymentController.java +++ b/server/java/src/main/java/app/payment/PaymentController.java @@ -45,6 +45,26 @@ public class PaymentController { }; + public static Route updatePaymentIntentCurrency= (Request request, Response response) -> { + + String paymentIntentId = request.params(":id"); + String currency = request.queryParams("currency"); + String paymentMethods = request.queryParams("payment_methods"); + + + PaymentIntent paymentIntent = Payment.updateCurrencyPaymentMethods(paymentIntentId, currency, paymentMethods); + + response.status(200); + response.type("application/json"); + + Map wrapper = new HashMap(); + wrapper.put("paymentIntent", paymentIntent); + + return ApiResource.GSON.toJson(wrapper); + + }; + + public static Route getPaymentIntent = (Request request, Response response) -> { String paymentIntentId = request.params(":id"); diff --git a/server/node/config.js b/server/node/config.js index 138d5a73..491369bf 100644 --- a/server/node/config.js +++ b/server/node/config.js @@ -32,6 +32,7 @@ module.exports = { 'p24', // eur, pln 'sofort', // eur (SOFORT must always use Euros) 'wechat', // aud, cad, eur, gbp, hkd, jpy, sgd, or usd. + 'au_becs_debit', //aud ], // Configuration for Stripe. diff --git a/server/node/routes.js b/server/node/routes.js index 3911e208..19c92bec 100644 --- a/server/node/routes.js +++ b/server/node/routes.js @@ -53,10 +53,14 @@ router.post('/payment_intents', async (req, res, next) => { const amount = await calculatePaymentAmount(items); try { + //build initial payment methods which should exclude currency specific ones + const initPaymentMethods = config.paymentMethods.filter(paymentMethod => paymentMethod !== 'au_becs_debit'); + + const paymentIntent = await stripe.paymentIntents.create({ amount, currency, - payment_method_types: config.paymentMethods, + payment_method_types: initPaymentMethods, }); return res.status(200).json({paymentIntent}); } catch (err) { @@ -80,6 +84,20 @@ router.post('/payment_intents/:id/shipping_change', async (req, res, next) => { } }); +// Update PaymentIntent with currency and paymentMethod. +router.post('/payment_intents/:id/update_currency', async (req, res, next) => { + const {currency, payment_methods} = req.body; + try { + const paymentIntent = await stripe.paymentIntents.update(req.params.id, { + currency, + payment_method_types: payment_methods, + }); + return res.status(200).json({paymentIntent}); + } catch (err) { + return res.status(500).json({error: err.message}); + } +}); + // Webhook handler to process payments for sources asynchronously. router.post('/webhook', async (req, res) => { let data; diff --git a/server/php/index.php b/server/php/index.php index b56530e9..44bf2a26 100644 --- a/server/php/index.php +++ b/server/php/index.php @@ -90,10 +90,13 @@ function (Request $request, Response $response, array $args) use ($paths) { $app->post('/payment_intents', function (Request $request, Response $response, array $args) { $data = $request->getParsedBody(); try { + //build initial payment methods which should exclude currency specific ones + $initPaymentMethods = array_diff($this->get('settings')['stripe']['paymentMethods'],['au_becs_debit']); + $paymentIntent = \Stripe\PaymentIntent::create([ 'amount' => Inventory::calculatePaymentAmount($data['items']), 'currency' => $data['currency'], - 'payment_method_types' => $this->get('settings')['stripe']['paymentMethods'] + 'payment_method_types' => $initPaymentMethods ]); return $response->withJson([ 'paymentIntent' => $paymentIntent ]); @@ -115,6 +118,20 @@ function (Request $request, Response $response, array $args) use ($paths) { } }); +// Update PaymentIntent with currency and paymentMethod. +$app->post('/payment_intents/{id}/update_currency', function (Request $request, Response $response, array $args) { + $data = $request->getParsedBody(); + $currency = $data['currency']; + $paymentMethods = $data['payment_methods']; + + try { + $paymentIntent = \Stripe\PaymentIntent::update($args['id'], [ 'currency' => $currency, 'payment_method_types:' => $paymentMethods ]); + return $response->withJson([ 'paymentIntent' => $paymentIntent ]); + } catch (\Exception $e) { + return $response->withJson([ 'error' => $e->getMessage() ])->withStatus(403); + } +}); + // Fetch the payment intent status // Used for redirect sources when coming back to the return URL $app->get('/payment_intents/{id}/status', function (Request $request, Response $response, array $args) { diff --git a/server/php/settings.php b/server/php/settings.php index 7f8f831d..520ea4dc 100644 --- a/server/php/settings.php +++ b/server/php/settings.php @@ -42,7 +42,8 @@ 'p24', // eur, pln // 'sepa_debit', // Restricted. See docs for activation details: https://stripe.com/docs/sources/sepa-debit 'sofort', // eur (SOFORT must always use Euros) - 'wechat' // aud, cad, eur, gbp, hkd, jpy, sgd, or usd. + 'wechat', // aud, cad, eur, gbp, hkd, jpy, sgd, or usd., + 'au_becs_debit' //aud ], // See settings.ini diff --git a/server/python/app.py b/server/python/app.py index 726a6c9b..56b81656 100644 --- a/server/python/app.py +++ b/server/python/app.py @@ -99,12 +99,17 @@ def make_payment_intent(): # Creates a new PaymentIntent with items from the cart. data = json.loads(request.data) try: + + # build initial payment methods which should exclude currency specific ones + initPaymentMethods=os.getenv( + 'PAYMENT_METHODS').split(', ') if os.getenv( + 'PAYMENT_METHODS') else ['card'] + initPaymentMethods.remove('au_becs_debit') + payment_intent = stripe.PaymentIntent.create( amount=Inventory.calculate_payment_amount(items=data['items']), currency=data['currency'], - payment_method_types=os.getenv( - 'PAYMENT_METHODS').split(', ') if os.getenv( - 'PAYMENT_METHODS') else ['card'] + payment_method_types=initPaymentMethods ) return jsonify({'paymentIntent': payment_intent}) @@ -127,6 +132,22 @@ def update_payment_intent(id): except Exception as e: return jsonify(e), 403 +@app.route('/payment_intents//update_currency', methods=['POST']) +def update_payment_intent(id): + data = json.loads(request.data) + currency = data['currency'] + paymentMethods = data['payment_methods'] + try: + payment_intent = stripe.PaymentIntent.modify( + id, + currency=currency, + payment_method_types=paymentMethods + ) + + return jsonify({'paymentIntent': payment_intent}) + except Exception as e: + return jsonify(e), 403 + @app.route('/webhook', methods=['POST']) def webhook_received(): diff --git a/server/ruby/app.rb b/server/ruby/app.rb index 7a2858c4..40004f74 100644 --- a/server/ruby/app.rb +++ b/server/ruby/app.rb @@ -97,10 +97,14 @@ content_type 'application/json' data = JSON.parse request.body.read + # build initial payment methods which should exclude currency specific ones + init_payment_methods = ENV['PAYMENT_METHODS'] ? ENV['PAYMENT_METHODS'].split(', ') : ['card'] + init_payment_methods.delete('au_becs_debit') + payment_intent = Stripe::PaymentIntent.create( amount: Inventory.calculate_payment_amount(data['items']), currency: data['currency'], - payment_method_types: ENV['PAYMENT_METHODS'] ? ENV['PAYMENT_METHODS'].split(', ') : ['card'] + payment_method_types: init_payment_methods ) { @@ -127,6 +131,26 @@ }.to_json end +post '/payment_intents/:id/update_currency' do + content_type 'application/json' + data = JSON.parse request.body.read + + currency = data['currency'] + payment_methods = data['payment_methods'] + + payment_intent = Stripe::PaymentIntent.update( + params['id'], + { + currency: currency, + payment_method_types: payment_methods + } + ) + + { + paymentIntent: payment_intent + }.to_json +end + post '/webhook' do # You can use webhooks to receive information about asynchronous payment events. # For more about our webhook events check out https://stripe.com/docs/webhooks.