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 @@
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.