UNPKG

@financial-times/n-conversion-forms

Version:

Containing jsx components and styles for forms included on Accounts and Acqusition apps (next-signup, next-profile, next-retention, etc).

260 lines (243 loc) 6.32 kB
/** * Light weight wrapper around PaymentRequest API for ApplePay * @example * // Setting up production * try { * const applePay = new ApplePay(window); * const canMakePayment = await applePay.canMakePayment(); * if (canMakePayment) { * // Show payment button * } * } catch (error) { * // Browser may not support it * } * * // Use test by passing the test payment methods * const applePay = new ApplePay(window, ApplePay.TEST_PAYMENT_METHODS); * * // On click of payment button you can updated payment details * try { * const updatedPaymentDetails = ApplePay.getPaymentDetails(20.00, 'GBP', 'FT Standard'); * const response = await applePay.show(updatedPaymentDetails); * // Send to Membership for fulfilment * if (success) { * response.complete('success'); * } else { * response.complete('fail'); * } * } catch (error) { * // Payment failed * } */ class ApplePay { /** * Setup a Payment Request API request with Apple Pay defaults * @param {Window} window Browser window object * @param {Array} methods PaymentRequest API payment methods array * @param {Object} details PaymentRequest API payment details object * @param {Object} options PaymentRequest API payment options object * @throws If browser doesn't support PaymentRequest API */ constructor( window, methods = ApplePay.PAYMENT_METHODS, details = ApplePay.PAYMENT_DETAILS, options = ApplePay.PAYMENT_OPTIONS ) { if (!window.PaymentRequest) { throw new Error('Browser does not support PaymentRequest API'); } this.window = window; this.methods = methods; this.details = details; this.options = options; this.request = new this.window.PaymentRequest( this.methods, this.details, this.options ); } /** * Proxy call through to the request * @return {Promise<Boolean>} */ canMakePayment() { return this.request.canMakePayment(); } /** * Display the payment screen to the user * @param {Object} paymentDetails Optional payment details object for update * @return {Promise<Object>} Response object from apple pay */ show(paymentDetails) { // Work around to enable the update of payment details for safari // browsers as it seems not to be supported, regenerates PaymentRequest if (paymentDetails) { this.details = paymentDetails; this.request = new this.window.PaymentRequest( this.methods, this.details, this.options ); } this.request.onmerchantvalidation = (event) => this.handleMerchantValidation(event); return this.request.show(); } /** * Validate ourselves via membership * @param {Object} event */ async handleMerchantValidation(event) { const merchantId = ApplePay.getMerchantId(this.methods); const url = ApplePay.getMerchantValidationUrl(merchantId); const data = { validationUrl: event.validationURL, displayName: 'FT.com', domainName: 'www.ft.com', merchantId, }; try { const response = await this.window.fetch(url, { method: 'POST', mode: 'cors', cache: 'no-cache', headers: { 'Content-Type': 'application/json; charset=utf-8', }, body: JSON.stringify(data), }); return event.complete(response.json()); } catch (error) { this.request.abort(); throw error; } } /** * Return merchant id from methods or default to production id * @param {Array} methods * @return {String} */ static getMerchantId(methods = []) { const method = methods[0] || {}; return ( (method.data && method.data.merchantIdentifier) || ApplePay.MERCHANT_ID ); } /** * Return merchant validation URL based on the merchant ID * @param {String} merchantId * @return {String} */ static getMerchantValidationUrl(merchantId) { if (merchantId === ApplePay.TEST_MERCHANT_ID) { return ApplePay.TEST_MERCHANT_VALIDATION_URL; } return ApplePay.MERCHANT_VALIDATION_URL; } /** * Helper method to format payment details correctly * @param {Number} value Cost in 2 digit decimal number * @param {String} currency ISO 3 digit currency code * @param {String} label Product name */ static getPaymentDetails(value, currency, label) { const details = ApplePay.PAYMENT_DETAILS; details.total.label = label; details.total.amount.value = value; details.total.amount.currency = currency; return details; } /** * Expose the production merchant id * @return {String} */ static get MERCHANT_ID() { return 'merchant.com.ft'; } /** * Expose the production merchant validation url * @return {String} */ static get MERCHANT_VALIDATION_URL() { return 'https://api.ft.com/idm/v1/apple-merchant-validation/validate'; } /** * Expose production payment methods * @return {Array} */ static get PAYMENT_METHODS() { return [ { supportedMethods: 'https://apple.com/apple-pay', data: { version: 1, merchantIdentifier: ApplePay.MERCHANT_ID, merchantCapabilities: ['supports3DS'], supportedNetworks: ['amex', 'discover', 'masterCard', 'visa'], countryCode: 'GB', }, }, ]; } /** * Default payment details * @return {Object} */ static get PAYMENT_DETAILS() { return { total: { label: 'FT.com', amount: { value: 0.01, currency: 'GBP', }, }, }; } /** * Default payment options * @return {Object} */ static get PAYMENT_OPTIONS() { return { requestPayerName: false, requestPayerEmail: false, requestPayerPhone: false, requestShipping: false, }; } /** * Expose the test merchant id * @return {String} */ static get TEST_MERCHANT_ID() { return 'merchant.test.env.apple.pay'; } /** * Expose the test merchant validation url * @return {String} */ static get TEST_MERCHANT_VALIDATION_URL() { return 'https://api-t.ft.com/idm/v1/apple-merchant-validation/validate'; } /** * Expose the test payment methods * @return {Array} */ static get TEST_PAYMENT_METHODS() { return [ { supportedMethods: 'https://apple.com/apple-pay', data: { version: 1, merchantIdentifier: ApplePay.TEST_MERCHANT_ID, merchantCapabilities: ['supports3DS'], supportedNetworks: ['amex', 'discover', 'masterCard', 'visa'], countryCode: 'GB', }, }, ]; } } module.exports = ApplePay;