From b3438d1bd3675119080a1ccbec633e3eae53add2 Mon Sep 17 00:00:00 2001 From: Chris Barton Date: Thu, 7 Sep 2023 14:25:21 -0700 Subject: [PATCH] feat(google pay): Add support for `existingPaymentMethodRequired` Adds support for `IsReadyToPayRequest.existingPaymentMethodRequired` to emit a `google-pay-not-available` error and not render the button if the user does not have a card that is supported by merchant's gateway configuration. This can be configured via the `existingPaymentMethodRequired` option in `recurly.GooglePay`. --- lib/recurly/google-pay/google-pay.js | 10 +++- lib/recurly/google-pay/pay-with-google.js | 4 +- test/unit/google-pay/google-pay.test.js | 70 +++++++++++++++++------ types/lib/google-pay/index.d.ts | 7 +++ 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/lib/recurly/google-pay/google-pay.js b/lib/recurly/google-pay/google-pay.js index 76e371692..39ef69c35 100644 --- a/lib/recurly/google-pay/google-pay.js +++ b/lib/recurly/google-pay/google-pay.js @@ -126,7 +126,15 @@ const getGoogleInfoFromMerchantInfo = ({ recurlyMerchantInfo, options }) => { }, }; - return { gatewayCode, environment, isReadyToPayRequest, paymentDataRequest }; + return { + gatewayCode, + environment, + isReadyToPayRequest: { + ...isReadyToPayRequest, + ...(options.existingPaymentMethodRequired === true && { existingPaymentMethodRequired: true }), + }, + paymentDataRequest, + }; }; const buildPaymentDataRequest = ({ recurly, options }) => { diff --git a/lib/recurly/google-pay/pay-with-google.js b/lib/recurly/google-pay/pay-with-google.js index a29100cb8..54bf4475c 100644 --- a/lib/recurly/google-pay/pay-with-google.js +++ b/lib/recurly/google-pay/pay-with-google.js @@ -77,8 +77,8 @@ const payWithGoogle = ({ paymentOptions, isReadyToPayRequest, paymentDataRequest .catch(err => { throw recurlyError('google-pay-init-error', { err }); }) - .then(({ result: isReadyToPay }) => { - if (!isReadyToPay) { + .then(({ result: isReadyToPay, paymentMethodPresent }) => { + if (!isReadyToPay || paymentMethodPresent === false) { throw recurlyError('google-pay-not-available'); } diff --git a/test/unit/google-pay/google-pay.test.js b/test/unit/google-pay/google-pay.test.js index 0f9f473ae..aee9d9b13 100644 --- a/test/unit/google-pay/google-pay.test.js +++ b/test/unit/google-pay/google-pay.test.js @@ -545,27 +545,65 @@ describe(`Google Pay`, function () { }); context('when cannot proceed with the pay-with-google', function () { - beforeEach(function () { - this.stubGoogleAPIOpts.isReadyToPay = Promise.resolve({ result: false }); - this.stubRequestAndGoogleApi(); - }); + context('when the GooglePay is not available', function () { + beforeEach(function () { + this.stubGoogleAPIOpts.isReadyToPay = Promise.resolve({ result: false }); + this.stubRequestAndGoogleApi(); + }); - it('emits the same error the pay-with-google throws', function (done) { - const result = googlePay(this.recurly, this.googlePayOpts); + it('emits the same error the pay-with-google throws', function (done) { + const result = googlePay(this.recurly, this.googlePayOpts); - result.on('error', (err) => assertDone(done, () => { - assert.ok(err); - assert.equal(err.code, 'google-pay-not-available'); - assert.equal(err.message, 'Google Pay is not available'); - })); + result.on('error', (err) => assertDone(done, () => { + assert.ok(err); + assert.equal(err.code, 'google-pay-not-available'); + assert.equal(err.message, 'Google Pay is not available'); + })); + }); + + it('do not emit any token nor the on ready event', function (done) { + const result = googlePay(this.recurly, this.googlePayOpts); + + result.on('ready', () => done(new Error('expected to not emit a ready event'))); + result.on('token', () => done(new Error('expected to not emit a token event'))); + nextTick(done); + }); }); - it('do not emit any token nor the on ready event', function (done) { - const result = googlePay(this.recurly, this.googlePayOpts); + context('when the GooglePay is available but does not support user cards', function () { + beforeEach(function () { + this.googlePayOpts.existingPaymentMethodRequired = true; + this.stubGoogleAPIOpts.isReadyToPay = Promise.resolve({ result: true, paymentMethodPresent: false }); + this.stubRequestAndGoogleApi(); + }); + + it('initiates pay-with-google with the expected Google Pay Configuration', function (done) { + googlePay(this.recurly, this.googlePayOpts); + + nextTick(() => assertDone(done, () => { + assert.equal(window.google.payments.api.PaymentsClient.called, true); + const isReadyToPayRequest = window.google.payments.api.PaymentsClient.prototype.isReadyToPay.getCall(0).args[0]; + assert.equal(isReadyToPayRequest.existingPaymentMethodRequired, true); + })); + }); + + it('emits the same error the pay-with-google throws', function (done) { + const result = googlePay(this.recurly, this.googlePayOpts); + + result.on('error', (err) => assertDone(done, () => { + assert.ok(err); + assert.equal(err.code, 'google-pay-not-available'); + assert.equal(err.message, 'Google Pay is not available'); + })); + }); - result.on('ready', () => done(new Error('expected to not emit a ready event'))); - result.on('token', () => done(new Error('expected to not emit a token event'))); - nextTick(done); + it('do not emit any token nor the on ready event', function (done) { + const result = googlePay(this.recurly, this.googlePayOpts); + + result.on('ready', () => done(new Error('expected to not emit a ready event'))); + result.on('token', () => done(new Error('expected to not emit a token event'))); + nextTick(done); + }); }); }); diff --git a/types/lib/google-pay/index.d.ts b/types/lib/google-pay/index.d.ts index da2441168..ed09f6083 100644 --- a/types/lib/google-pay/index.d.ts +++ b/types/lib/google-pay/index.d.ts @@ -58,6 +58,13 @@ export type GooglePayOptions = { [key: string]: any }; + /** + * If set to true, then the Google Pay button will not be rendered and an error will be emitted if the user + * is not eligible to pay with Google Pay. + * See https://developers.google.com/pay/api/web/reference/request-objects#IsReadyToPayRequest for more information. + */ + existingPaymentMethodRequired?: boolean; + /** * Requires the user to accept providing the full billing address. * @deprecated use billingAddressRequired