We’ve migrated our documentation to a new site, which means some URLs have changed.
Subscriptions

Braintree Google Pay

Braintree Google Pay Payments

Braintree is a payment provider, owned by Paypal. Braintree supports merchants in 45 countries and 130+ currencies. Braintree is a full-service provider that seamlessly integrates with Piano, including Google Pay payments.

It is important that all billing should take place in Piano and you shouldn't be charging users directly via Braintree's own subscription options described here.

Please also note that Braintree Google Pay is a separate integration independent of other Braintree payment methods.

Limitations

There are essentially no limitations. The new merchant must be registered to operate and have a bank account out of one of the following countries: US, Canada, Australia, Europe, Singapore, Hong Kong SAR China, Malaysia, or New Zealand.

Supported Currency

Piano supports the following Braintree supported currencies:

Supported Currencies

AUD

BGN

CAD

CHF

CZK

DKK

EUR

GBP

HKD

HRK

HUF

ISK

JPY

KRW

MYR

NOK

NZD

PLN

RON

SEK

SGD

USD

To enable multiple currencies, you must manually add new Braintree merchant accounts to your general Braintree account for each currency you would like to enable. Once this is done, you will be given a separate merchant account ID for each currency. Enter these credentials with their respective currencies into your dashboard.

Note, that Piano may support only selected currencies available in the payment providers dashboard. It is highly recommended that you inquire directly with Braintree Payments about the capabilities of their acquiring partners/processors in relation to individual payment methods support and the ability to process recurring transactions (MITs) in the particular country you are looking to operate in.

Supported Payment Methods

Piano supports the following payment methods for this payment provider:

Payment method name

Are renewals supported?

Google Pay

Yes

Supported Features and Functionality

Please click on the "Fullscreen" option below to enlarge the table to show all available payment method features.

Feature

Supported?

Import endpoint: Migration support

N/A*

Grace and Retry

Yes

Checkout and Billing

Purchase with a new payment method

Yes

Purchase with saved payment method

Yes

Remember my payment method

Yes

Tax support (Taxjar, Avalara, OneSource)

Yes

Promotions (free / discounted)

Yes

Trials (free / paid)

Yes

Support 3DS (cards only)

N/A

Email notifications

Yes

Auto-renewal / Free renewal

Yes

Manual renewal flow

Yes

MyAccount

Display UPI in Payment methods section

Yes

Tax Support (Taxjar, Avalara, OneSource)

Yes

History events

Yes

Inquiries support

Yes

Cancel subscription

Yes

Vouchers

Yes

Add payment method

Yes

Edit payment instrument

Pending

Delete payment instrument

Yes

Update renewal payment method with new payment method

Yes

Update renewal payment method with saved payment method

Yes

Upgrade

Yes

Manual renewal flow

Yes

MyAccount: Cancel and Refund

Yes

Publisher Dashboard: Reports and logs

New Activity Report

Yes

Revenue Report

Yes

Transactions Log: Statuses (completed, refunded, pending)

Yes

Transactions Log: Latest Revenue Recognition List

Yes

Transactions Log: Latest Revenue Recognition Summary

Yes

Publisher Dashboard: User Profile

Overview (display data)

Yes

Subscription Details: Cancel

Yes

Subscription Details: Cancel & Refund

Yes

Subscription Details: Change Billing date

Yes

Subscription Details: Update Renewal Method

Yes

History events

Yes

Transactions: Refund

Yes

Inquires support

Yes

Subscription Details: Upgrade

Yes

Vouchers: Refund & Revoke

Yes

Wallet: Add payment instrument

N/A

Wallet: 3DS for adding cards

N/A

Wallet: Apply to all active subscriptions

N/A

Wallet: Edit payment instrument

N/A

Wallet: Delete payment instrument

Yes

Wallet: Set as default

Pending

Publisher Dashboard: Subscribe User

Purchase with a new payment method

N/A

Purchase with saved payment method

Pending

Tax Support (Taxjar, Avalara, OneSource)

Yes

Apply this payment method to all active subscriptions

Pending

Checkout: Frictionless

N/A

Transactions Log: Status (charged back/disputed)

N/A

Passive Churn prevention

Yes - Advanced support

*For more information about the payment token import please visit this link.

Passive Churn Prevention

It's crucial to understand how the various error codes that may arise during a payment failure are handled by the Passive Churn Prevention feature. These error codes provide insights into why a renewal attempt may have failed, enabling you to take appropriate actions to prevent churn. This section details the specific error codes encountered during a payment failure and their mapping to a decline reason.

Decline reason

Error codes

Suspected fraud

2014 (Processor Declined – Fraud Suspected)

2021 (Security Violation)

Insufficient funds

2001 (Insufficient Funds)

Expired card

2004 (Expired Card)

Card is stolen or lost

2012 (Processor Declined – Possible Lost Card)

2013 (Processor Declined – Possible Stolen Card)

2047 (Call Issuer. Pick Up Card)

2053 (Card reported as lost or stolen)

Limit exceeded

2002 (Limit Exceeded)

2003 (Cardholder's Activity Limit Exceeded)

Network failure

3000 (Processor Network Unavailable – Try Again)

3DS failure

2041 (Declined – Call For Approval)

2099 (Cardholder Authentication Required)

Call issuer

2015 (Transaction Not Allowed)

2038 (Processor Declined)

2043 (Error – Do Not Retry, Call Issuer)

2044 (Declined – Call Issuer)

2046 (Declined)

2057 (Issuer or Cardholder has put a restriction on the card)

2109-2999 (Processor Declined)

Do not honor

2000 (Do Not Honor)

Everything else

All error codes not listed above

How to integrate Braintree Google Pay with Piano

To integrate Braintree Google Pay with Piano, you will need the following:

  • Obtain Merchant ID, Public Key, Private Key, and a set of Merchants/Currencies from your Braintree account.

  • The Google Pay Merchant ID is provided by Google.

  • For Business setup on Google Pay, refer to Google Pay for Online Business Help.

Once you have obtained the above information, you can input it into the Piano dashboard by clicking Edit Business→Payment Provider→Braintree Google Pay (Add New).

Enter the information into the form fields and then click Save:

image-20231123-065404.png

  • Title: The name of this integration (for your reference only)

  • Merchant ID: your Merchant ID with Braintree

  • Public key: your Braintree public key

  • Private key: your Braintree private key

  • Google Pay Merchant ID: your Google Pay Merchant ID is provided by Google.

  • Currencies: you will need to select the currencies that are to be supported along with the corresponding Braintree merchant account ID(s).

In addition to the configuration on Piano's end, you will also need to configure Google Pay in your Braintree dashboard as follows:

  1. Login to Braintree Dashboard

    • Access the Braintree Dashboard and navigate to Settings (located in the upper-right-hand corner of the screen).

  2. Configuration of Merchant Accounts and Currencies

    • In the Braintree Dashboard, under SettingsBusiness, locate your Braintree Merchant ID.

    • Review or add merchant accounts and supported currencies, and ensure that the merchant account supports Google Pay as a payment method.

Refer to the Braintree - Google Pay website for more information about implementing the Google Pay payment method.

For Piano's Braintree credit card and PayPal integration see the article here.

Template changes

For Braintree Google Pay, to ensure the latest code is loaded in the Checkout and My Account, system template changes are required. There are a few ways to update your templates;

  • The easiest is to reset to default if you haven’t stylized the templates.

  • If you did, then you can use the Compare code feature and compare the code between the default version and your current version, and carry over any new code line by line.

Check that the Payment Components template contains the following components, incl. the relevant code:

<script type="text/ng-template" id="/frontend/providers/components/braintreegooglepay/checkout/component.shtml">
<div class="braintreegooglepay-wrapper new-card-form">
  <div class="row">
    <div ng-if="isStoredPaymentsScreenShown()" class="braintreegooglepay-checkout-payment-form">
      <div class="add-credit-card-wrapper">
        <h4>
          <t context="checkout.platform">Your payment methods</t>
        </h4>
        <a href="javascript:void(0)" class="add-credit-card" ng-click="gotoNewPaymentScreen()"
           ng-show="stored.length > 0">
          +
          <t>Add a new payment method</t>
        </a>
      </div>
      <table ng-if="stored.length" class="payment-method-card">
        <tr class="payment-method-card-row" ng-repeat="method in stored">
          <td class="payment-method-card-cell">
            <input type="radio"
                   id="radio-{{method.id}}"
                   name="stored_method"
                   ng-model="data.selectedUpi"
                   ng-value="method"
                   ng-change="onUpiChange(method)"
                   radio-track-by="id"
                   />
          </td>
          <td class="payment-method-card-cell">
            <div class="payment-method-icon payment-method-icon--googlepay"></div>
          </td>
          <td class="payment-method-card-cell">
            <div>{{method.type | tc:"checkout.platform"}} *{{method.ending_in}}</div>
          </td>
          <td class="payment-method-card-cell payment-method-card-cell-expiration">
            <t>Expires in</t>
            {{method.expire_month}}/{{method.expire_year}}
          </td>
          <td class="payment-method-card-cell payment-method-card-cell-default-payment">
            <div class="payment-method-card-default-payment" ng-show="method.id === cc.defaultPaymentMethodId"><t>Default</t></div>
            <button ng-hide="method.id === cc.defaultPaymentMethodId"
                    ng-click="setAsDefaultPaymentMethod(method)"
                    type="button"
                    class="set-as-default-button"
            >
              <t>Set as default</t>
            </button>
          </td>
        </tr>
      </table>
    </div>

    <div ng-show="isNewPaymentScreenShown()" class="manage-payment-method-wrapper">
      <div ng-show="isReturnToStoredPaymentsShown()" class="add-credit-card-wrapper">
        <h4>
          <t context="checkout.platform">&nbsp;</t>
        </h4>
        <a href="javascript:void(0)" class="add-credit-card" ng-click="gotoStoredPaymentsScreen()">
          <t>< Return to saved cards</t>
        </a>
      </div>
      <div ng-if="isStoreInVaultAvailable()">
        <div class="custom-checkbox">
          <input type="checkbox" name="store_in_vault" ng-model="data.store_in_vault"
                 id="remember-my-account-braintreegooglepay"/>
          <label for="remember-my-account-braintreegooglepay">
            <t>Remember my account for future payments</t>
          </label>
        </div>
      </div>
      <div ng-if="isSubscription()">
        <input type="hidden" name="store_in_vault" ng-model="data.store_in_vault"/>
      </div>
      <div class="custom-checkbox" ng-if="isApplyPaymentMethodAvailable()">
        <input type="checkbox" name="shouldMakeDefaultPaymentMethod" ng-model="data.shouldMakeDefaultPaymentMethod"
               id="default-payment-method-braintreegooglepay"/>
        <label for="default-payment-method-braintreegooglepay">
          <t>Apply this payment method to all active subscriptions</t>
        </label>
      </div>
    </div>

    <div consents-list></div>
    <div class="footer-modal">
      <div class="auto-renew-purchase" ng-show="isSelectedTermAutoRenewChoiceAvailable()">
        <input class="auto-renew-purchase-checkbox" type="checkbox" ng-model="input.autoRenew"
               id="auto_renew"/>
        <label class="auto-renew-purchase-label" for="auto_renew">
          <t>Automatically renew my subscription when the time comes.</t>
        </label>
      </div>
      <div ng-show="isStoredPaymentsScreenShown()" class="complete-purchase-button" complete-purchase-button
           title="{{'Complete Purchase' | t}}"></div>
      <div ng-show="isNewPaymentScreenShown()">
        <div ng-show="isBtGpayLoading"
             id="bt-gp-spinner-wrapper"
             class="bt-gp-spinner-wrapper">
          <div pn-spinner size="small"></div>
        </div>
        <div ng-show="!isBtGpayLoading"
             id="bt-gp-button-container"
             class="bt-gp-button-container"
             data-e2e="bt-gp-button-complete-purchase">
        </div>
      </div>
    </div>
  </div>
</div>

</script>

If your application was created a long time ago, please check the Checkout template is also using the latest version.

Please check that you are using the latest version of the My Account Wallet Components, you can either reset to default or use the compare versions feature to make updates to your template. Please make sure that the template contains the following components, incl. the relevant code:

<script type="text/ng-template" id="/frontend/providers/components/braintreegooglepay/my_account/component.shtml">
<div class="braintreegooglepay-ma-container">
  <div>
    <my-account-billing-country></my-account-billing-country>

    <div ng-if="data.paymentInstrument">
      <div class="braintreegooglepay-pm-title">
        <t context="checkout.platform">Your credit or debit card</t>
      </div>
      <div class="cc-fields-layout__flex-row">
        <div class="cc-fields-layout__field cc-fields-layout__card-number">
          <credit-card-number ng-model="data.paymentInstrument.num.value"
                              ng-disabled="true"
                              ng-required="false"
                              ng-readonly="false"
                              valid="true"
                              name="'num'">
          </credit-card-number>
        </div>

        <div class="cc-fields-layout__field cc-fields-layout__expire">
          <cc-expire-component ng-model="data.paymentInstrument.expire.value"
                               ng-disabled="true"
                               ng-required="false"
                               ng-readonly="false"
                               valid="true"
                               name="'expire'">
          </cc-expire-component>
        </div>
        <div ng-show="data.paymentInstrument.cvv.value" class="cc-fields-layout__field cc-fields-layout__expire">
          <credit-card-cvv ng-model="data.paymentInstrument.cvv.value"
                           ng-disabled="true"
                           ng-required="false"
                           ng-readonly="false"
                           valid="true"
                           name="'cvv'">
          </credit-card-cvv>
        </div>
      </div>

    </div>
    <div ng-if="!data.paymentInstrument">
      <div ng-show="isBtGpayLoading"
           id="bt-gp-spinner-wrapper"
           class="bt-gp-spinner-wrapper">
        <div pn-spinner size="small"></div>
      </div>
      <div ng-show="!isBtGpayLoading"
           id="bt-gp-button-container"
           class="bt-gp-button-container"
           data-e2e="bt-gp-button-complete-purchase"></div>
    </div>
  </div>
</div>

</script>

To ensure compliance with Google’s brand guidelines, Piano has updated templates to correctly display the Google Pay logo. To reflect these changes on your application, please make sure your templates contain the relevant code as described below.

Go to ManageTemplatesSystemUpgrade components and find these strings:

<span ng-show="upgradeInformation.creditCard">
    <t arg0="{{upgradeInformation.paymentType}}" arg1="{{upgradeInformation.accountNumber.slice(-4)}}"><b>{0} *{1}</b></t>
</span>
<span ng-hide="upgradeInformation.creditCard">
    <t arg0="{{upgradeInformation.paymentMethodName}}"><b>{0}</b></t>
</span>
<span ng-if="upgradeInformation.expireMonth &amp;&amp; upgradeInformation.expireYear">
    <t arg0="{{upgradeInformation.expireMonth}}" arg1="{{upgradeInformation.expireYear}}"> expires in <b>{0}/{1}</b></t>
</span>

And replace them with the following:

<span ng-bind-html="getUpgradePaymentInfoText(upgradeInformation)"></span>

The final piece of code after the change should look like this:

<li class="list-item">
    <span class="item-text">
        <t>Payment method: </t>
        <span ng-bind-html="getUpgradePaymentInfoText(upgradeInformation)"></span>
    </span>
</li>

Go to ManageTemplatesSystemUpgrade authentication components and find these strings:

<span ng-show="upgradeInformation.creditCard">
  <t arg0="{{upgradeInformation.paymentType}}" arg1="{{upgradeInformation.accountNumber.slice(-4)}}"><b>{0} *{1}</b></t>
</span>
<span ng-hide="upgradeInformation.creditCard">
  <t arg0="{{upgradeInformation.paymentMethodName}}"><b>{0}</b></t>
</span>
<span ng-if="upgradeInformation.expireMonth &amp;&amp; upgradeInformation.expireYear">
  <t arg0="{{upgradeInformation.expireMonth}}" arg1="{{upgradeInformation.expireYear}}"> expires in <b>{0}/{1}</b></t>
</span>

And replace them with the following:

<span ng-bind-html="getUpgradePaymentInfoText(upgradeInformation)"></span>

The final piece of code after the change should look like this:

<li class="list-item"
    data-e2e="payment-method"
>
  <span class="item-text">
    <t>Payment method: </t>
    <span ng-bind-html="getUpgradePaymentInfoText(upgradeInformation)"></span>
  </span>
</li>

Go to ManageTemplatesMy AccountMy Account Library Components and find these strings:

<span ng-show="upgradeFrom.card">
  <t context="checkout.platform" arg0="{{upgradeFrom.card.paymentType}}" arg1="{{upgradeFrom.card.lastFour}}"><b>{0} *{1}</b></t>
</span>
<span ng-if="upgradeFrom.card.expiration">
  <t context="checkout.platform" arg0="{{upgradeFrom.card.expiration}}"> expires in <b>{0}</b></t>
</span>

And replace them with the following:

<span ng-bind-html="getUpgradePaymentInfoText(upgradeFrom)"></span>

The final piece of code after the change should look like this:

<li class="simple-list__item">
  <span>
    <t context="checkout.platform">Payment method: </t>
    <span ng-bind-html="getUpgradePaymentInfoText(upgradeFrom)"></span>
  </span>
</li>

Last updated: