diff --git a/src/client/client.js b/src/client/client.js index 0d778bfa..3655b46c 100644 --- a/src/client/client.js +++ b/src/client/client.js @@ -4,6 +4,7 @@ import PaymentSubmitter from '../payment/payment-submitter'; import ClientTokenGenerator from '../payment/client-token-generator'; import StoreRequestSender from '../store/store-request-sender'; import DEFAULT_CONFIG from './default-config'; +import PaymentIntentGenerator from '../payment/payment-intent-generator'; export default class Client { /** @@ -16,13 +17,15 @@ export default class Client { const paymentSubmitter = PaymentSubmitter.create(clientConfig); const clientTokenGenerator = ClientTokenGenerator.create(clientConfig); const storeRequestSender = StoreRequestSender.create(clientConfig); + const paymentIntentGenerator = PaymentIntentGenerator.create(clientConfig); return new Client( clientConfig, paymentSubmitter, offsitePaymentInitializer, clientTokenGenerator, - storeRequestSender + storeRequestSender, + paymentIntentGenerator ); } @@ -32,13 +35,15 @@ export default class Client { * @param {OffsitePaymentInitializer} offsitePaymentInitializer * @param {ClientTokenGenerator} clientTokenGenerator * @param {StoreRequestSender} storeRequestSender + * @param {PaymentIntentGenerator} paymentIntentGenerator */ constructor( config, paymentSubmitter, offsitePaymentInitializer, clientTokenGenerator, - storeRequestSender + storeRequestSender, + paymentIntentGenerator ) { /** * @private @@ -69,6 +74,12 @@ export default class Client { * @type {StoreRequestSender} */ this.storeRequestSender = storeRequestSender; + + /** + * @private + * @type {PaymentIntentGenerator} + */ + this.paymentIntentGenerator = paymentIntentGenerator; } /** @@ -159,4 +170,13 @@ export default class Client { deleteShopperInstrument(data, callback) { this.storeRequestSender.deleteShopperInstrument(data, callback); } + + /** + * @param {PaymentRequestData} data + * @param {Function} [callback] + * @returns {void} + */ + generatePaymentIntent(data, callback) { + this.paymentIntentGenerator.generatePaymentIntent(data, callback); + } } diff --git a/src/payment/payment-intent-generator.js b/src/payment/payment-intent-generator.js new file mode 100644 index 00000000..6964ce19 --- /dev/null +++ b/src/payment/payment-intent-generator.js @@ -0,0 +1,55 @@ +import RequestSender from '../common/http-request/request-sender'; +import UrlHelper from './url-helper'; +import PaymentIntentMapper from './v2/payment-mappers/payment-intent-mapper'; + +export default class PaymentIntentGenerator { + /** + * @param {Object} config + * @returns {PaymentSubmitter} + */ + static create(config) { + const urlHelper = UrlHelper.create(config); + const requestSender = RequestSender.create(); + const paymentIntentMapper = PaymentIntentMapper.create(); + + return new PaymentIntentGenerator(urlHelper, requestSender, paymentIntentMapper); + } + + /** + * @param {UrlHelper} urlHelper + * @param {RequestSender} requestSender + * @param {PaymentIntentMapper} paymentIntentMapper + * @returns {void} + */ + constructor(urlHelper, requestSender, paymentIntentMapper) { + /** + * @private + * @type {UrlHelper} + */ + this.urlHelper = urlHelper; + + /** + * @private + * @type {RequestSender} + */ + this.requestSender = requestSender; + + /** + * @private + * @type {PaymentIntentMapper} + */ + this.paymentIntentMapper = paymentIntentMapper; + } + + /** + * @param {PaymentRequestData} data + * @param {Function} [callback] + * @returns {void} + */ + generatePaymentIntent(data, callback) { + const url = this.urlHelper.getGeneratePaymentIntentUrl(); + const payload = this.paymentIntentMapper.mapToPaymentIntent(data); + + this.requestSender.postRequest(url, payload, {}, callback); + } +} diff --git a/src/payment/url-helper.js b/src/payment/url-helper.js index b2a6534c..b4655c15 100644 --- a/src/payment/url-helper.js +++ b/src/payment/url-helper.js @@ -49,4 +49,11 @@ export default class UrlHelper { getGenerateClientTokenUrl() { return `${this.host}/api/v2/public/payments/client_tokens`; } + + /** + * @returns {string} + */ + getGeneratePaymentIntentUrl() { + return `${this.host}/api/v2/public/payments/payment_intents`; + } } diff --git a/src/payment/v2/payment-mappers/payment-intent-mapper.js b/src/payment/v2/payment-mappers/payment-intent-mapper.js new file mode 100644 index 00000000..69a8e42f --- /dev/null +++ b/src/payment/v2/payment-mappers/payment-intent-mapper.js @@ -0,0 +1,55 @@ +import { omitNil } from '../../../common/utils'; +import GatewayMapper from './gateway-mapper'; + +export default class PaymentIntentMapper { + /** + * @returns {PaymentIntentMapper} + */ + static create() { + const gatewayMapper = GatewayMapper.create(); + + return new PaymentIntentMapper(gatewayMapper); + } + + /** + * @param {GatewayMapper} gatewayMapper + */ + constructor(gatewayMapper) { + /** + * @private + * @type {GatewayMapper} + */ + this.gatewayMapper = gatewayMapper; + } + + /** + * @param {PaymentRequestData} data + * @returns {Object} + */ + mapToPaymentIntent(data) { + return omitNil({ + stripe_data: this.mapStripeData(data), + gateway: this.gatewayMapper.mapToGateway(data), + }); + } + + mapStripeData(data) { + const { + shouldSavePaymentInstrument = false, + customer = {}, + cart = {}, + store = {}, + } = data; + + return omitNil({ + should_save_payment_instrument: shouldSavePaymentInstrument, + customer_id: customer.customerId, + customer_name: customer.name, + customer_email: customer.email, + grand_total: cart.grandTotal.integerAmount, + currency_code: cart.currency, + store_id: store.storeId, + store_name: store.storeName, + }); + } +} diff --git a/test/client/client.spec.js b/test/client/client.spec.js index 39e79640..a3344c5f 100644 --- a/test/client/client.spec.js +++ b/test/client/client.spec.js @@ -11,6 +11,7 @@ describe('Client', () => { let offsitePaymentInitializer; let paymentSubmitter; let storeRequestSender; + let paymentIntentGenerator; beforeEach(() => { config = { host: 'https://bigpay.dev' }; @@ -35,12 +36,17 @@ describe('Client', () => { deleteShopperInstrument: jasmine.createSpy('deleteShopperInstrument'), }; + paymentIntentGenerator = { + generatePaymentIntent: jasmine.createSpy('generatePaymentIntent'), + }; + client = new Client( config, paymentSubmitter, offsitePaymentInitializer, clientTokenGenerator, - storeRequestSender + storeRequestSender, + paymentIntentGenerator ); }); @@ -126,4 +132,13 @@ describe('Client', () => { expect(storeRequestSender.deleteShopperInstrument).toHaveBeenCalledWith(data, callback); }); + + it('Generate a payment intent', () => { + const callback = () => {}; + const data = storeIntrumentDataMock; + + client.generatePaymentIntent(data, callback); + + expect(paymentIntentGenerator.generatePaymentIntent).toHaveBeenCalledWith(data, callback); + }); }); diff --git a/test/payment/payment-intent-generator.spec.js b/test/payment/payment-intent-generator.spec.js new file mode 100644 index 00000000..18faaeee --- /dev/null +++ b/test/payment/payment-intent-generator.spec.js @@ -0,0 +1,42 @@ +import paymentRequestDataMock from '../mocks/payment-request-data'; +import PaymentIntentGenerator from '../../src/payment/payment-intent-generator'; + +describe('PaymentIntentGenerator', () => { + let data; + let urlHelper; + let requestSender; + let paymentIntentMapper; + let paymentIntentGenerator; + + beforeEach(() => { + data = paymentRequestDataMock; + + urlHelper = { + getGeneratePaymentIntentUrl: + jasmine.createSpy('getPaymentUrl').and.returnValue('/api/v2/public/payments/payment_intents'), + }; + + requestSender = { + postRequest: jasmine.createSpy('postRequest'), + }; + + paymentIntentMapper = { + mapToPaymentIntent: jasmine.createSpy('mapToPaymentIntent'), + }; + + paymentIntentGenerator = new PaymentIntentGenerator(urlHelper, requestSender, paymentIntentMapper); + }); + + it('Create an instance of PaymentIntentGenerator', () => { + const config = { host: 'https://bigpay.dev' }; + const instance = PaymentIntentGenerator.create(config); + + expect(instance instanceof PaymentIntentGenerator).toBeTruthy(); + }); + + it('Maps the input data into a payment intent object required to create a payment intent', () => { + paymentIntentGenerator.generatePaymentIntent(data, () => {}); + + expect(paymentIntentMapper.mapToPaymentIntent).toHaveBeenCalled(); + }); +}); diff --git a/test/payment/url-helper.spec.js b/test/payment/url-helper.spec.js index 40922bfa..4fa58e4a 100644 --- a/test/payment/url-helper.spec.js +++ b/test/payment/url-helper.spec.js @@ -26,4 +26,8 @@ describe('UrlHelper', () => { it('returns a URL for generating a client token', () => { expect(urlHelper.getGenerateClientTokenUrl()).toEqual(`${host}/api/v2/public/payments/client_tokens`); }); + + it('returns a URL for generating a payment intent', () => { + expect(urlHelper.getGeneratePaymentIntentUrl()).toEqual(`${host}/api/v2/public/payments/payment_intents`); + }); }); diff --git a/test/payment/v2/payment-mappers/payment-intent-mapper.spec.js b/test/payment/v2/payment-mappers/payment-intent-mapper.spec.js new file mode 100644 index 00000000..9b7efc17 --- /dev/null +++ b/test/payment/v2/payment-mappers/payment-intent-mapper.spec.js @@ -0,0 +1,41 @@ +import GatewayMapper from '../../../../src/payment/v2/payment-mappers/gateway-mapper'; +import paymentRequestDataMock from '../../../mocks/payment-request-data'; +import PaymentIntentMapper from '../../../../src/payment/v2/payment-mappers/payment-intent-mapper'; + +describe('PaymentIntentMapper', () => { + let data; + let gatewayMapper; + let paymentIntentMapper; + let mappedData; + let paymentMethodIdMapper; + + beforeEach(() => { + data = paymentRequestDataMock; + mappedData = { + name: 'id', + }; + + paymentMethodIdMapper = { + mapToId: jasmine.createSpy('mapToId').and.returnValue(mappedData.name), + }; + + gatewayMapper = new GatewayMapper(paymentMethodIdMapper); + + paymentIntentMapper = new PaymentIntentMapper(gatewayMapper); + }); + + it('create a instance of paymentIntentMapper', () => { + const instance = PaymentIntentMapper.create(); + + expect(instance instanceof PaymentIntentMapper).toBeTruthy(); + }); + + it('map the data to the payment intent', () => { + const output = paymentIntentMapper.mapToPaymentIntent(data); + + expect(output).toEqual({ + stripe_data: paymentIntentMapper.mapStripeData(data), + gateway: gatewayMapper.mapToGateway(data), + }); + }); +});