Local Payment Methods

Client-Side Implementationanchor

Browser supportanchor

Learn more about browser support for v3 of our JavaScript SDK.

Basic configurationanchor

Determine which merchant account to useanchor

This will determine which PayPal credentials are used for the transaction, and must match the merchant account specified with any other calls in the transaction lifecycle (e.g. Transaction.sale). Many merchants use a separate merchant account for local payments. Other merchants use separate merchant accounts for transactions in different currencies. It is not possible to switch merchant accounts between the start and finish of a local payment transaction, so it is important to determine the correct one from the start.

  1. Javascript
function merchantAccountId() {
  return("EUR_local");
}

Load the componentanchor

If you're using <script> tags to load files, be sure to at least include:

  1. HTML
<script src="https://js.braintreegateway.com/web/3.111.0/js/client.min.js"></script>

<script src="https://js.braintreegateway.com/web/3.111.0/js/local-payment.min.js"></script>

Initialize the componentanchor

Every integration requires a client. Once you've created one, you can pass it to the local payment component to accept your payments.

  1. Callback
  2. Promise
var localPaymentInstance;

// Create a client.
braintree.client.create({
  authorization: CLIENT_AUTHORIZATION
}, function (clientErr, clientInstance) {

  // Stop if there was a problem creating the client.
  // This could happen if there is a network error or if the authorization
  // is invalid.
  if (clientErr) {
    console.error('Error creating client:', clientErr);
    return;
  }

  // Create a local payment component.
  braintree.localPayment.create({
    client: clientInstance,
    merchantAccountId: merchantAccountId()
  }, function (localPaymentErr, paymentInstance) {

    // Stop if there was a problem creating local payment component.
    // This could happen if there was a network error or if it's incorrectly
    // configured.
    if (localPaymentErr) {
      console.error('Error creating local payment:', localPaymentErr);
      return;
    }

    localPaymentInstance = paymentInstance;
  });
});

Render Local Payment Method buttonsanchor

You will need to render and style a button for each payment method you intend to support.

  1. HTML
<button id="ideal-button">
  <img src="img/ideal.png" class="payment-button" />
</button>
<button id="sofort-button">
  <img src="img/sofort.png" class="payment-button" />
</button>

Use the local payment component to attach a listener to your buttons to begin the payment flow.

  1. Javascript
var idealButton = document.getElementById('ideal-button');
var sofortButton = document.getElementById('sofort-button');

The following is a list of valid paymentType, countryCode, and currencyCode values for each Local Payment Method:

Local Payment Method Payment type Country codes Currency codes
Bancontact bancontact BE EUR
BLIK blik PL PLN
EPS eps AT `EUR
grabpay grabpay SG SGD
iDEAL ideal NL EUR
Klarna Pay Now / SOFORT sofort AT, BE, DE, IT, NL, ES, GB EUR (outside GB), GBP (GB only)
MyBank mybank IT EUR
Pay Upon Invoice pay_upon_invoice DE EUR
P24 p24 PL EUR, PLN

The paymentTypeCountryCode parameter is only required for payment methods with multiple potential country codes. If a paymentTypeCountryCode is not provided, then the countryCode value will be used as the paymentTypeCountryCode.

The following is a list of required parameters for each Local Payment Method, as well as any applicable transaction limits:

Local Payment Method Required parameters Customer transaction limits
Bancontact givenName, surname, currencyCode Min: 1.00 EUR
BLIK givenName, surname, currencyCode, email Min: 1.00 PLN
EPS givenName, surname, currencyCode Min: 1.00 EUR
grabpay givenName, surname, currencyCode Min: 1.00 SGD
iDEAL givenName, surname, currencyCode N/A
Klarna Pay Now / SOFORT givenName, surname, currencyCode, countryCode, paymentTypeCountryCode Min: 1.00 EUR (outside the United Kingdom), 1.00 GBP (United Kingdom only)
MyBank givenName, surname, currencyCode N/A
Pay Upon Invoice givenName, surname, currencyCode, address, email, billingAddress, birthDate, phone, phoneCountryCode Min: 5.00 EUR
P24 givenName, surname, currencyCode, email Min: 1.00 PLN

Redirect Flowanchor

availability

Local Payment Method redirect flow is available in the Braintree JS SDK starting in version 3.111.0

We've heard about several issues from merchants about popups being blocked, which has led to users not being able to complete payments. To enhance the user experience and ensure seamless payment processing, we are introducing a new redirect flow. This feature enables merchants to provide a redirectUrl which will redirect users on the current page instead of opening a new popup window. The new flow maintains the same functionality as the existing popup flow which ensures a seamless transition for your users.

Domain Registrationanchor

In order to use the redirect flow, you'll need to register any domains you plan to use as a redirectUrl with PayPal. You can do this through the Braintree control panel using the instructions below. You will have to do this in both your sandbox and production environments.

Rules for domain namesanchor

A domain name:

  • Must not start with a scheme (e.g. "https://").
  • Must be between 4 and 255 characters.
  • Cannot contain spaces or wildcards (*).
  • Can consist of a top-level domain, a second-level domain, and 0 or more subdomains etc. a.b.example.com where example is the top-level domain, com is the second-level domain, and a and b are subdomains.
  • The top-level domain must consist of 2 to 63 letters.
  • Each subdomain and the second-level domain must consist of 1 to 63 alphanumeric characters and or hyphens.
  • Each subdomain and the second-level domain can not start or end with a hyphen.
  • The top-level domain, second-level domain, and the subdomains must be separated by a dot(.).
  • Can end with a trailing dot.

Sandbox Environmentanchor

Register your sandbox domain name in the Braintree Control Panel:

  1. Log into your sandbox Control Panel

  2. Click on the gear icon in the top right corner

  3. Click Account Settings from the drop-down menu

  4. Scroll to the Payment Methods section

  5. Next to PayPal, click the Options link

  6. Client the View Domain Names button

  7. Enter the domain of your return page in the Specify Your Domain Names section.

  8. Click Add Domain Names button

Production environmentanchor

Register your production domain name in the Braintree Control Panel:

  1. Log into your production Control Panel

  2. Click on the gear icon in the top right corner

  3. Click Account Settings from the drop-down menu

  4. Scroll to the Payment Methods section

  5. Next to PayPal, click the Options link

  6. Client the View Domain Names button

  7. Enter the domain of your return page in the Specify Your Domain Names section.

  8. Click Add Domain Names button

Code examples for redirect flowanchor

To utilize the new redirect flow, you need to pass the redirectUrl parameter when calling braintree.localPayment.create. This redirectUrl should point to the same page of your merchant website where you redirect the user from, so that when redirected back, the Braintree SDK can finish tokenizing and provide you with a nonce.

  1. Callback
  2. Promise
braintree.client.create({
  authorization: CLIENT_AUTHORIZATION
}, function (clientErr, clientInstance) {

  if (clientErr) {
    console.error('Error creating client:', clientErr);
    return;
  }

  return braintree.localPayment.create({
    client: clientInstance,
    merchantAccountId: "merchant-id",
    // Passing this redirectUrl will initiate the redirect flow
    redirectUrl: "https://www.merchant-site.com/redirect-page.html"
  }, function (localPaymentErr, localPaymentInstance) {
    if (localPaymentErr) {
      console.error("There was an error: ", localPaymentErr);
      return;
    }

    if (localPaymentInstance.tokenizePayload) {
      // send the nonce to your server
      console.log("Got a nonce: ", localPaymentInstance.tokenizePayload.nonce);
    } else {
      // invoke localPaymentInstance.startPayment({...}) as described below
    }
  });
});

After redirecting back from the mandate page, if tokenization was successful, you will be able to access a nonce from localPaymentInstance.tokenizePayload.nonce (see example above).

Error Handlinganchor

In the case where the user cancels out of the payment flow, the redirect URL will have the params wasCanceled=true as well as errorcode=payment_error. If the user approves the payment, but there was a problem processing the payment, the redirect URL will have the params wasCanceled=true as well as errorcode=processing_error. Note that you will have to check the values of both wasCanceled as well as errorcode to be able to differentiate between a user canceling and a payment error. You can find all the errorcodes documented here.

Invoke local payment flowanchor

You must implement the startPayment method for preprocessing and result handling in the popup flow.

  1. Callback
  2. Promise
function createLocalPaymentClickListener(type) {
  return function (event) {
    event.preventDefault();

    localPaymentInstance.startPayment({
      paymentType: type,
      amount: '10.67',
      fallback: { // see Fallback section for details on these params
        url: 'https://your-domain.com/page-to-complete-checkout',
        buttonText: 'Complete Payment'
      },
      currencyCode: 'EUR',
      givenName: 'Joe',
      surname: 'Doe',
      address: {
        countryCode: 'NL'
      },
      onPaymentStart: function (data, start) { // this is only required in the popup flow
        // NOTE: It is critical here to store data.paymentId on your server
        //       so it can be mapped to a webhook sent by Braintree once the
        //       buyer completes their payment. See Start the payment
        //       section for details.

        // Call start to initiate the popup
        start();
      }
    }, function (startPaymentError, payload) {
      if (startPaymentError) {
        if (startPaymentError.code === 'LOCAL_PAYMENT_POPUP_CLOSED') {
          console.error('Customer closed Local Payment popup.');
        } else {
          console.error('Error!', startPaymentError);
        }
      } else {
        // Send the nonce to your server to create a transaction
        console.log(payload.nonce);
      }
    });
  };
}

idealButton.addEventListener('click', createLocalPaymentClickListener('ideal'));
sofortButton.addEventListener('click', createLocalPaymentClickListener('sofort'));
important

You must implement Braintree webhooks in order to accept Local Payment Methods. See Server-side for details.

Start the paymentanchor

Before starting the payment flow for the buyer, you must store the paymentId provided in the data object in the onPaymentStart method. This identifier is critical for handling a case where the buyer completes the payment flow with their bank and exits that flow without returning to your checkout page. Braintree recommends that you store the paymentId and map it to a shopping cart identifier or some other form of identifier. This information will be necessary when Braintree sends you a webhook of a completed payment.

See the Local Payment Method webhooks reference for details on the exact content of the Local Payment Method completed notification.

Fallbackanchor

If a customer is using a mobile device and has a mobile app to process their payment, the window will automatically redirect to the mobile app. The app will then open up a new window that is unable to communicate with the original page that started the flow. Because of this, you must configure a fallback URL and button text, which will be used to create a button in the window created by the mobile app to redirect back to your website.

  1. Callback
localPaymentInstance.startPayment({
  fallback: {
    url: 'https://your-domain.com/page-to-complete-checkout', // the page to go to on completion of the flow
    buttonText: 'Complete Payment' // the text to put into the button to trigger the redirect
  },
  // etc

The URL you configure will automatically have query parameters added to it that will be used to complete the payment. On the page you configure, set up the localPaymentInstance and call tokenize:

  1. Callback
  2. Promise
braintree.client.create({
  authorization: CLIENT_AUTHORIZATION
}, function (clientErr, clientInstance) {
  braintree.localPayment.create({
    client: clientInstance
  }, function (localPaymentErr, localPaymentInstance) {
    if (localPaymentInstance.hasTokenizationParams()) {
      localPaymentInstance.tokenize(function (tokenizeError, payload) {
        if (tokenizeError) {
          // handle tokenization error
        }

        // send payload.nonce to your server
      });
    } else {
      // if this page should only be reached when
      // recovering from a mobile app switch,
      // display an error for not having the
      // correct params in the query string
    }
  });
});

Shipping addressesanchor

If you need a shipping address to ship physical goods, set shippingAddressRequired to true. This setting will prompt your customer to provide shipping details. If you have already collected these details from your customer, you can pass the shipping details within the startPayment method to avoid having your customer provide them again.

  1. Callback
  2. Promise
function createLocalPaymentClickListener(type) {
  return function (event) {
    event.preventDefault();

    localPaymentInstance.startPayment({
      paymentType: type,
      amount: '10.67',
      fallback: { // see Fallback section for details on these params
        url: 'https://your-domain.com/page-to-complete-checkout',
        buttonText: 'Complete Payment'
      },
      currencyCode: 'EUR',
      shippingAddressRequired: true,
      email: 'joe@getbraintree.com',
      phone: '5101231234',
      givenName: 'Joe',
      surname: 'Doe',
      address: {
        streetAddress: 'Oosterdoksstraat 110',
        extendedAddress: 'Apt. B',
        locality: 'DK Amsterdam',
        postalCode: '1011',
        region: 'NH',
        countryCode: 'NL'
      },
      onPaymentStart: function (data, start) {
        // NOTE: It is critical here to store data.paymentId on your server
        //       so it can be mapped to a webhook sent by Braintree once the
        //       buyer completes their payment. See Start the payment
        //       section for details.

        // Call start to initiate the popup
        start();
      }
    }, function (startPaymentError, payload) {
      if (startPaymentError) {
        if (startPaymentError.code === 'LOCAL_PAYMENT_POPUP_CLOSED') {
          console.error('Customer closed Local Payment popup.');
        } else {
          console.error('Error!', startPaymentError);
        }
      } else {
        // Send the nonce to your server to create a transaction
        console.log(payload.nonce);
      }
    });
  };
}

idealButton.addEventListener('click', createLocalPaymentClickListener('ideal'));
sofortButton.addEventListener('click', createLocalPaymentClickListener('sofort'));

If you are not shipping physical goods, set shippingAddressRequired to false. This setting will prompt your customer to provide just the basic information like givenName, surname, phone, and email. If you have already collected these details from your customer, you can pass them within the startPayment method to avoid having your customer provide them again.

  1. Callback
  2. Promise
function createLocalPaymentClickListener(type) {
  return function (event) {
    event.preventDefault();

    localPaymentInstance.startPayment({
      paymentType: type,
      amount: '10.67',
      fallback: { // see Fallback section for details on these params
        url: 'https://your-domain.com/page-to-complete-checkout',
        buttonText: 'Complete Payment'
      },
      currencyCode: 'EUR',
      shippingAddressRequired: false,
      email: 'joe@getbraintree.com',
      phone: '5101231234',
      givenName: 'Joe',
      surname: 'Doe',
      address: {
        countryCode: 'NL'
      },
      onPaymentStart: function (data, start) {
        // NOTE: It is critical here to store data.paymentId on your server
        //       so it can be mapped to a webhook sent by Braintree once the
        //       buyer completes their payment. See Start the payment
        //       section for details.

        // Call start to initiate the popup
        start();
      }
    }, function (startPaymentError, payload) {
      if (startPaymentError) {
        if (startPaymentError.code === 'LOCAL_PAYMENT_POPUP_CLOSED') {
          console.error('Customer closed Local Payment popup.');
        } else {
          console.error('Error!', startPaymentError);
        }
      } else {
        // Send the nonce to your server to create a transaction
        console.log(payload.nonce);
      }
    });
  };
}

idealButton.addEventListener('click', createLocalPaymentClickListener('ideal'));
sofortButton.addEventListener('click', createLocalPaymentClickListener('sofort'));

Next Page: Server-side