Repository: braintree/braintree-web-drop-in
Pull request #810 (2022-03-28 13:04:32)

feat: implemented a param to specify any vaulted payment method type that they want to block.
### Summary

~~Implemented `vaultedPaypalAccountsDisabled` argument.  
If set to true, it force to disable paypal vault.~~

Implemented `vaultedPaymentMethodTypesThatShouldBeHidden`. 
It accepts an array of paymentMethodTypes (e.g CreditCard, PayPalAccount) and specify any vaulted payment method type that want to block. Important: ApplePayCard, VenmoAccount, AndroidPayCard are always blocked.

This is very useful when you have two different payment flows and on one it is not possible to enable paypal vaulting (due to legal restriction for example).

### Checklist

- [ ] Added a changelog entry

### Authors
> List GitHub usernames for everyone who contributed to this pull request.

- 


crookedneighbor: I think this should be done more holistically and allow the merchant to specify any vaulted payment method type that they want to block. 

Also, I think it should be a parameter under `vaultManager`, not a top level parameter.
crookedneighbor: I think this should be done more holistically and allow the merchant to specify any vaulted payment method type that they want to block. 

Also, I think it should be a parameter under `vaultManager`, not a top level parameter.
ale-grosselle: @crookedneighbor good point, I'm going to implement your suggestions!
ale-grosselle: @crookedneighbor "_I think it should be a parameter under vaultManager_", could you explain to me where I would add the parameter?

I didn't find in this project :/ 
crookedneighbor: My mistake the `vaultManager` option is a `boolean`, not an object: https://github.com/braintree/braintree-web-drop-in/blob/bf05e9a9ad10a7b22ccc80fa2e86561d16d7ed72/src/index.js#L269

Making it a top level object with the payment methods to hide as the keys should be fine.
ale-grosselle: @crookedneighbor Implemented the param if you want to take a look!

crookedneighbor: Made a couple of adjustments, added a changelog entry and rebased. I force pushed the changes, so if you have this checked out locally, you'll need to fetch the changes and reset to the remote branch if you need to make any changes.
ale-grosselle: Hello @crookedneighbor, any news?
shango420: > 
Perfecto buen trabajo 

ale-grosselle: Any news, could we merge it please? 🙏 🙏 

shango420: > # **_~~~~_**
ale-grosselle: Hello @crookedneighbor, any news?
armandodlvr: @ale-grosselle  could you please rename `vaultedPaymentMethodTypesThatShouldBeHidden` to `hiddenVaultedPaymentMethodTypes` ? and resolve conflicts to merge your PR. Thanks
ale-grosselle: Hi @armandodlvr done!
shango420: > ### Summary
> 
> ~~Implemented `vaultedPaypalAccountsDisabled` argument.  
> If set to true, it force to disable paypal vault.~~
> 
> Implemented `vaultedPaymentMethodTypesThatShouldBeHidden`. 
> It accepts an array of paymentMethodTypes (e.g CreditCard, PayPalAccount) and specify any vaulted payment method type that want to block. Important: ApplePayCard, VenmoAccount, AndroidPayCard are always blocked.
> 
> This is very useful when you have two different payment flows and on one it is not possible to enable paypal vaulting (due to legal restriction for example).
> 
> ### Checklist
> 
> - [ ] Added a changelog entry
> 
> ### Authors
> > List GitHub usernames for everyone who contributed to this pull request.
> 
> - 
> 


armandodlvr: @ale-grosselle the changelog was removed. could you add it?... thanks
ale-grosselle: > hiddenVaultedPaymentMethodTypes

Done!

> @ale-grosselle the changelog was removed. could you add it?... thanks


ale-grosselle: @armandodlvr is it okay now?
shango420: vaultedPaymentMethodTypesThatShouldBeHidden
ale-grosselle: Hello @cgdibble @crookedneighbor, 
is possible to merge this PR?

Thanks
cgdibble: I'm going to have some teammates take a look as an extra set of eyes in the morning, but otherwise it looks good and we should be able to merge it tomorrow. 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc1d0f7..2c7068b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # CHANGELOG
 
+## unreleased
+
+  - Add `hiddenVaultedPaymentMethodTypes` option to `dropin.create`
+
 ## 1.34.0
 
   - Update braintree-web to v3.90.0
diff --git a/src/dropin-model.js b/src/dropin-model.js
index eca3115..5f875f7 100644
--- a/src/dropin-model.js
+++ b/src/dropin-model.js
@@ -13,7 +13,10 @@ var paymentSheetViews = require('./views/payment-sheet-views');
 var vaultManager = require('braintree-web/vault-manager');
 var paymentOptionsViewID = require('./views/payment-options-view').ID;
 
-var VAULTED_PAYMENT_METHOD_TYPES_THAT_SHOULD_BE_HIDDEN = [
+// these vaulted payment methods can only be used for existing subscription
+// any new transactions or subscriptons should prompt the customer to
+// authorize them again before using them and thus should always be hidden.
+var VAULTED_PAYMENT_METHOD_TYPES_THAT_SHOULD_ALWAYS_BE_HIDDEN = [
   paymentMethodTypes.applePay,
   paymentMethodTypes.googlePay,
   paymentMethodTypes.venmo
@@ -42,6 +45,10 @@ function DropinModel(options) {
 
     return total;
   }.bind(this), {});
+  this.hiddenVaultedPaymentMethodTypes =
+    constructHiddenPaymentMethodTypes(
+      options.merchantConfiguration.hiddenVaultedPaymentMethodTypes
+    );
 
   this.failedDependencies = {};
   this._options = options;
@@ -384,15 +391,25 @@ DropinModel.prototype.getVaultedPaymentMethods = function () {
 };
 
 DropinModel.prototype._getSupportedPaymentMethods = function (paymentMethods) {
-  var supportedPaymentMethods = this.supportedPaymentOptions.reduce(function (array, key) {
+  var self = this;
+  var supportedPaymentMethods = this.supportedPaymentOptions.reduce(function (
+    array,
+    key
+  ) {
     var paymentMethodType = paymentMethodTypes[key];
 
-    if (canShowVaultedPaymentMethodType(paymentMethodType)) {
+    if (
+      canShowVaultedPaymentMethodType(
+        paymentMethodType,
+        self.hiddenVaultedPaymentMethodTypes
+      )
+    ) {
       array.push(paymentMethodType);
     }
 
     return array;
-  }, []);
+  },
+  []);
 
   return paymentMethods.filter(function (paymentMethod) {
     return supportedPaymentMethods.indexOf(paymentMethod.type) > -1;
@@ -461,8 +478,44 @@ function isPaymentOptionEnabled(paymentOption, options) {
   });
 }
 
-function canShowVaultedPaymentMethodType(paymentMethodType) {
-  return paymentMethodType && VAULTED_PAYMENT_METHOD_TYPES_THAT_SHOULD_BE_HIDDEN.indexOf(paymentMethodType) === -1;
+function canShowVaultedPaymentMethodType(
+  paymentMethodType,
+  hiddenVaultedPaymentMethodTypes
+) {
+  return (
+    paymentMethodType &&
+    hiddenVaultedPaymentMethodTypes.indexOf(paymentMethodType) ===
+      -1
+  );
+}
+
+function constructHiddenPaymentMethodTypes(paymentMethods) {
+  var hiddenVaultedPaymentMethodTypes = [].concat(
+    VAULTED_PAYMENT_METHOD_TYPES_THAT_SHOULD_ALWAYS_BE_HIDDEN
+  );
+
+  if (Array.isArray(paymentMethods)) {
+    paymentMethods.forEach(function (paymentMethod) {
+      var paymentMethodId = paymentMethodTypes[paymentMethod];
+
+      if (!paymentMethodId) {
+        // don't add it if it is an unknown payment method
+        return;
+      }
+
+      if (
+        hiddenVaultedPaymentMethodTypes.indexOf(paymentMethodId) >
+        -1
+      ) {
+        // don't add the same payment method type a second time
+        return;
+      }
+
+      hiddenVaultedPaymentMethodTypes.push(paymentMethodId);
+    });
+  }
+
+  return hiddenVaultedPaymentMethodTypes;
 }
 
 module.exports = DropinModel;
diff --git a/src/index.js b/src/index.js
index 7b62533..37d9a7a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -235,7 +235,11 @@ var VERSION = '__VERSION__';
  * `zh_XC`.
  *
  * @param {object} [options.translations] To use your own translations, pass an object with the strings you wish to replace. This object must use the same structure as the object used internally for supported translations, which can be found [here](https://github.com/braintree/braintree-web-drop-in/blob/main/src/translations/en_US.js). Any strings that are not included will be those from the provided `locale` or `en_US` if no `locale` is provided. See below for an example of creating Drop-in with custom translations.
- * @param {array} [options.paymentOptionPriority] Use this option to indicate the order in which enabled payment options should appear when multiple payment options are enabled. By default, payment options will appear in this order: `['card', 'paypal', 'paypalCredit', 'venmo', 'applePay', 'googlePay']`. Payment options omitted from this array will not be offered to the customer.
+ * @param {string[]} [options.paymentOptionPriority] Use this option to indicate the order in which enabled payment options should appear when multiple payment options are enabled. By default, payment options will appear in this order: `['card', 'paypal', 'paypalCredit', 'venmo', 'applePay', 'googlePay']`. Payment options omitted from this array will not be offered to the customer.
+ * @param {string[]} [options.hiddenVaultedPaymentMethodTypes] Use this option to hide certain types of vaulted payment methods from being displayed to the customer. Apple Pay, Google Pay and Venmo are always hidden (as those payment methods are only availabe as vaulted payment methods for existing subscriptions, new transactions or subscriptions must be newly authorized). Available options:
+ * * `'card'`
+ * * `'paypal'`
+ * * `'paypalCredit'` (same as PayPal)
  *
  * @param {(boolean|object)} [options.card] The configuration options for cards. See [`cardCreateOptions`](#~cardCreateOptions) for all `card` options. If this option is omitted, cards will still appear as a payment option. To remove cards, pass `false` for the value.
  * @param {object} [options.paypal] The configuration options for PayPal. To include a PayPal option in your Drop-in integration, include the `paypal` parameter and [enable PayPal in the Braintree Control Panel](https://developer.paypal.com/braintree/docs/guides/paypal/testing-go-live#go-live). To test in Sandbox, you will need to [link a PayPal sandbox test account to your Braintree sandbox account](https://developer.paypal.com/braintree/docs/guides/paypal/testing-go-live#linked-paypal-testing).


Test patch:
diff --git a/test/unit/dropin-model.js b/test/unit/dropin-model.js
index 0d43e0c..486ac93 100644
--- a/test/unit/dropin-model.js
+++ b/test/unit/dropin-model.js
@@ -1591,5 +1591,67 @@ describe('DropinModel', () => {
         });
       }
     );
+
+    describe('hiddenVaultedPaymentMethodTypes options', function () {
+      async function initModel(hiddenVaultedPaymentMethodTypes) {
+        const modelOptions = { ...testContext.modelOptions };
+
+        modelOptions.merchantConfiguration = {
+          ...testContext.modelOptions.merchantConfiguration
+        };
+        modelOptions.merchantConfiguration.hiddenVaultedPaymentMethodTypes =
+          hiddenVaultedPaymentMethodTypes;
+
+        testContext.model = new DropinModel(modelOptions);
+
+        testContext.vaultManager.fetchPaymentMethods.mockResolvedValue([
+          {
+            nonce: '1-nonce',
+            type: 'CreditCard'
+          },
+          {
+            nonce: '2-nonce',
+            type: 'PayPalAccount'
+          }
+        ]);
+
+        await testContext.model.initialize();
+        testContext.model.isGuestCheckout = false;
+
+        return testContext.model;
+      }
+
+      test("when an empty array, then 'model.hiddenVaultedPaymentMethodTypes' contains default values", () => {
+        initModel([]);
+        expect(
+          testContext.model.hiddenVaultedPaymentMethodTypes.sort()
+        ).toEqual(['AndroidPayCard', 'ApplePayCard', 'VenmoAccount']);
+      });
+      test("when undefined, then 'model.hiddenVaultedPaymentMethodTypes' contains default values", () => {
+        // eslint-disable-next-line no-undefined
+        initModel(undefined);
+        expect(
+          testContext.model.hiddenVaultedPaymentMethodTypes.sort()
+        ).toEqual(['AndroidPayCard', 'ApplePayCard', 'VenmoAccount']);
+      });
+      test("when an array with 'paypal', then 'model.hiddenVaultedPaymentMethodTypes' contains default values and 'PayPalAccount' value", () => {
+        initModel(['paypal']);
+        expect(
+          testContext.model.hiddenVaultedPaymentMethodTypes.sort()
+        ).toEqual(['AndroidPayCard',
+          'ApplePayCard',
+          'PayPalAccount',
+          'VenmoAccount']);
+      });
+      test("when an array with 'card', then 'model.hiddenVaultedPaymentMethodTypes' contains default values and 'CreditCard' value", () => {
+        initModel(['card']);
+        expect(
+          testContext.model.hiddenVaultedPaymentMethodTypes.sort()
+        ).toEqual(['AndroidPayCard',
+          'ApplePayCard',
+          'CreditCard',
+          'VenmoAccount']);
+      });
+    });
   });
 });