Skip to content

Commit

Permalink
feat(google pay): Add support for existingPaymentMethodRequired
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
cbarton committed Sep 7, 2023
1 parent 802ded3 commit b3438d1
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 19 deletions.
10 changes: 9 additions & 1 deletion lib/recurly/google-pay/google-pay.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
4 changes: 2 additions & 2 deletions lib/recurly/google-pay/pay-with-google.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}

Expand Down
70 changes: 54 additions & 16 deletions test/unit/google-pay/google-pay.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});

Expand Down
7 changes: 7 additions & 0 deletions types/lib/google-pay/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit b3438d1

Please sign in to comment.