Payments
Wallets
GrabPay
Accept a payment

GrabPay payments

Learn how to accept GrabPay, a common payment method in Southeast Asia.

Stripe users in Malaysia and Singapore can now accept GrabPay payments from customers in Malaysia and Singapore respectively. Review the supported currencies and GrabPay transaction limits below:

Country Customer country Supported currency Transaction limits
Singapore (SG) Singapore (SG) SGD GrabPay SG
Malaysia (MY) Malaysia (MY) MYR GrabPay MY

Note that there is no minimum charge amount for GrabPay, so the payment amount value can be as low as 1.

GrabPay is a single-use payment method. Customers pay with GrabPay by redirecting from your website to GrabPay to authorize the payment. After that, they will automatically be redirected back to your website. You will get immediate notification on whether the payment succeeded or failed. For more details, see the section on how to fulfill the order.

Assets such as logos and payment buttons are provided in the branding guidelines section.

1 Set up Stripe Server-side

First, you need a Stripe account. Register now.

Use our official libraries for access to the Stripe API from your application:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

2 Create a PaymentIntent Server-side

Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.

Create a PaymentIntent on your server with an amount and currency. GrabPay currently only supports SGD in Singapore and MYR in Malaysia.

Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=grabpay \ -d amount=1099 \ -d currency=sgd
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' payment_intent = Stripe::PaymentIntent.create({ payment_method_types: ['grabpay'], amount: 1099, currency: 'sgd', })
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' payment_intent = stripe.PaymentIntent.create( payment_method_types=['grabpay'], amount=1099, currency='sgd', )
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $payment_intent = \Stripe\PaymentIntent::create([ 'payment_method_types' => ['grabpay'], 'amount' => 1099, 'currency' => 'sgd', ]);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("sgd") .addPaymentMethodType("grabpay") .build(); PaymentIntent paymentIntent = PaymentIntent.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ payment_method_types: ['grabpay'], amount: 1099, currency: 'sgd', });
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencySGD)), PaymentMethodTypes: stripe.StringSlice([]string{ "grabpay", }), } intent, _ := paymentintent.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var service = new PaymentIntentService(); var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "sgd", PaymentMethodTypes = new List<string> { "grabpay", }, }; var intent = service.Create(options);

Included in the returned PaymentIntent is a client secret, which is used on the client side to securely complete the payment process instead of passing the entire PaymentIntent object. There are different approaches that you can use to pass the client secret to the client side.

You can retrieve the client secret from an endpoint on your server using the browser’s fetch function on the client side. This approach is generally most suitable when your client side is a single-page application, particularly one built with a modern frontend framework such as React. This example shows how to create the server endpoint that serves the client secret:

get '/secret' do intent = # ... Create or retrieve the PaymentIntent {client_secret: intent.client_secret}.to_json end
from flask import Flask, jsonify app = Flask(__name__) @app.route('/secret') def secret(): intent = # ... Create or retrieve the PaymentIntent return jsonify(client_secret=intent.client_secret)
<?php $intent = # ... Create or retrieve the PaymentIntent echo json_encode(array('client_secret' => $intent->client_secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.PaymentIntent; import com.google.gson.Gson; import static spark.Spark.get; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); get("/secret", (request, response) -> { PaymentIntent intent = // ... Fetch or create the PaymentIntent Map<String, String> map = new HashMap(); map.put("client_secret", intent.getClientSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.get('/secret', async (req, res) => { const intent = // ... Fetch or create the PaymentIntent res.json({client_secret: intent.client_secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" stripe "github.com/stripe/stripe-go/v71" ) type CheckoutData struct { ClientSecret string `json:"client_secret"` } func main() { http.HandleFunc("/secret", func(w http.ResponseWriter, r *http.Request) { intent := // ... Fetch or create the PaymentIntent data := CheckoutData{ ClientSecret: intent.ClientSecret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; using Stripe; namespace StripeExampleApi.Controllers { [Route("secret")] [ApiController] public class CheckoutApiController : Controller { [HttpGet] public ActionResult Get() { var intent = // ... Fetch or create the PaymentIntent return Json(new {client_secret = intent.ClientSecret}); } } }

This example demonstrates how to fetch the client secret with JavaScript on the client side:

var response = fetch('/secret').then(function(response) { return response.json(); }).then(function(responseJson) { var clientSecret = responseJson.client_secret; // Call stripe.confirmGrabPayPayment() with the client secret. });
(async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Call stripe.confirmGrabPayPayment() with the client secret. })();

If your application uses server-side rendering, you may wish to use your template framework to embed the client secret in the HTML output of your checkout page during rendering. You can embed it in a data attribute or hidden HTML element and then extract it with JavaScript in order to use it to complete payment.

<input id="card-name" type="text"> <button id="card-button" data-secret="<%= @intent.client_secret %>">Submit Payment</button>
get '/checkout' do @intent = # ... Fetch or create the PaymentIntent erb :checkout end
<input id="card-name" type="text"> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
@app.route('/checkout') def checkout(): intent = # ... Fetch or create the PaymentIntent return render_template('checkout.html', client_secret=intent.client_secret)
<?php $intent = # ... Fetch or create the PaymentIntent; ?> ... <input id="card-name" type="text"> <button id="card-button" data-secret="<?= $intent->client_secret ?>"> Submit Payment </button> ...
<input id="card-name" type="text"> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
import java.util.HashMap; import java.util.Map; import com.stripe.model.PaymentIntent; import spark.ModelAndView; import static spark.Spark.get; public class StripeJavaQuickStart { public static void main(String[] args) { get("/checkout", (request, response) -> { PaymentIntent intent = // ... Fetch or create the PaymentIntent Map map = new HashMap(); map.put("client_secret", intent.getClientSecret()); return new ModelAndView(map, "checkout.hbs"); }, new HandlebarsTemplateEngine()); } }
<input id="card-name" type="text"> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
const express = require('express'); const expressHandlebars = require('express-handlebars'); const app = express(); app.engine('.hbs', expressHandlebars({ extname: '.hbs' })); app.set('view engine', '.hbs'); app.set('views', './views'); app.get('/checkout', async (req, res) => { const intent = // ... Fetch or create the PaymentIntent res.render('checkout', { client_secret: intent.client_secret }); }); app.listen(3000, () => { console.log('Running on port 3000'); });
<input id="card-name" type="text"> <button id="card-button" data-secret="{{ .ClientSecret }}"> Submit Payment </button>
package main import ( "html/template" "net/http" stripe "github.com/stripe/stripe-go/v71" ) type CheckoutData struct { ClientSecret string } func main() { checkoutTmpl := template.Must(template.ParseFiles("views/checkout.html")) http.HandleFunc("/checkout", func(w http.ResponseWriter, r *http.Request) { intent := // ... Fetch or create the PaymentIntent data := CheckoutData{ ClientSecret: intent.ClientSecret, } checkoutTmpl.Execute(w, data) }) http.ListenAndServe(":3000", nil) }
<input id="card-name" type="text"> <button id="card-button" data-secret="@ViewData["ClientSecret"]"> Submit Payment </button>
using System; using Microsoft.AspNetCore.Mvc; using Stripe; namespace StripeExampleApi.Controllers { [Route("/[controller]")] public class CheckoutController : Controller { public IActionResult Index() { var intent = // ... Fetch or create the PaymentIntent ViewData["ClientSecret"] = intent.ClientSecret; return View(); } } }

3 Submit the payment to Stripe Client-side

When a customer clicks to pay with GrabPay, we recommend you use Stripe.js to submit the payment to Stripe. Stripe.js is our foundational JavaScript library for building payment flows. It will automatically handle complexities like the redirect described below, and enables you to easily extend your integration to other payment methods in the future.

Include the Stripe.js script on your checkout page by adding it to the head of your HTML file.

<head> <title>Checkout</title> <script src="https://js.stripe.com/v3/"></script> </head>

Create an instance of Stripe.js with the following JavaScript on your checkout page.

// Set your publishable key. Remember to change this to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
// Set your publishable key. Remember to change this to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');

To create a payment on the client side, add a Pay with GrabPay button and use stripe.confirmGrabPayPayment to handle the redirect away from your page and to complete the payment.

<button type="button" id="grabpay-button">Pay with GrabPay</button> <div id="error-message"></div>
var button = document.getElementById('grabpay-button'); button.addEventListener('click', function() { // By this point, the PaymentIntent should have already been created // Pass the clientSecret of the PaymentIntent to confirmGrabPayPayment stripe.confirmGrabPayPayment(clientSecret, { // Return URL where the customer should be redirected to after the authorization return_url: 'https://your-website.com/checkout/complete', }); });
const button = document.getElementById('grabpay-button'); button.addEventListener('click', async function() { // By this point, the PaymentIntent should have already been created // Pass the clientSecret of the PaymentIntent to confirmGrabPayPayment stripe.confirmGrabPayPayment(clientSecret, { // Return URL where the customer should be redirected after the authorization return_url: 'https://your-website.com/checkout/complete', }); });

Pass the client secret of the PaymentIntent object that you created in Step 2 to stripe.confirmGrabPayPayment.

Additionally, add a return_url to this function to indicate where Stripe should redirect the user after they complete the payment on Grab’s website.

The return_url should correspond to a page on your website that provides the status of the payment, by verifying the status of the PaymentIntent when rendering the return page. When Stripe redirects the customer back to your website, the customer’s browser will make a GET request to the return_url. The request includes the following query parameters which can be used to verify status.

Parameter Description
payment_intent The unique identifier for the PaymentIntent.
payment_intent_client_secret The client secret of the PaymentIntent object.

You can also append your own query parameters when providing the return_url. They will persist through the redirect process.

When the customer is redirected back to your site, you can use the payment_intent_client_secret to query for the PaymentIntent and display the transaction status to your customer.

<h2>Payment result</h2> <div class="payment-result"> </div> <script> // Check if we're returning from a redirect. const url = new URL(window.location.href); const paymentIntentClientSecret = url.searchParams.get( "paymentIntentClientSecret" ); if (paymentIntentClientSecret) { stripe.retrievePaymentIntent(paymentIntentClientSecret).then(function(result) { const paymentIntent = result.paymentIntent; document.querySelector(".payment-result").textContent = paymentIntent.status; }); } </script>

4 Fulfill the order

We recommend using webhook events to confirm the charge has succeeded and to notify the customer that the payment is complete. The following events are sent when the PaymentIntent status is updated:

Event Description Next steps
payment_intent.succeeded The customer’s payment succeeded. Fulfill the goods or services that the customer purchased.
payment_intent.payment_failed The customer’s payment was declined. Contact the customer via email or push notification and request another payment method.

Testing

To test GrabPay in your test mode integration, the client side redirect will bring you to a Stripe-hosted GrabPay test payment page where you can either authorize or fail the test payment.

GrabPay is a single-use payment method. Customers pay with GrabPay by redirecting from your website to GrabPay to authorize the payment. After that, they will automatically be redirected back to your website. You will get immediate notification on whether the payment succeeded or failed.

Assets such as logos and payment buttons are provided in the branding guidelines section.

1 Determine compatibility

A Checkout Session must satisfy all of the following conditions to support GrabPay payments:

  • Prices for all line items must be in the same currency. If you have line items in different currencies, create separate Checkout Sessions for each currency.
  • You can only use one-time line items (recurring subscription plans are not supported).
  • The sgd currency is supported for businesses based in Singapore.
  • The myr currency is supported for businesses based in Malaysia.

2 Accept a payment

This guides you through enabling GrabPay and shows the differences between accepting a card payment and using GrabPay.

Enable GrabPay as a payment method

When creating a new Checkout Session, you need to:

  1. Add grabpay to the list of payment_method_types
  2. Make sure all your line_items use the same currency
Stripe::Checkout::Session.create({ mode: 'payment', payment_method_types: ['card'], payment_method_types: ['grabpay'], # or you can take multiple payment methods with # payment_method_types: ['card', 'grabpay', ...] line_items: [{ price_data: { currency: 'usd', # To accept `grabpay`, all line items must have currency: `sgd`, `myr` currency: 'sgd', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', })
stripe.checkout.Session.create( payment_method_types=['card'], payment_method_types=['grabpay'], # or you can take multiple payment methods with # payment_method_types=['card', 'grabpay', ...] line_items=[{ 'price_data': { currency: 'usd', # To accept `grabpay`, all line items must have currency: `sgd`, `myr` currency: 'sgd', 'product_data': { 'name': 'T-shirt', }, 'unit_amount': 2000, }, 'quantity': 1, }], mode='payment', success_url='https://example.com/success', cancel_url='https://example.com/cancel', )
$session = \Stripe\Checkout\Session::create([ 'payment_method_types' => ['card'], 'payment_method_types' => ['grabpay'], // or you can take multiple payment methods with // 'payment_method_types' => ['card', 'grabpay', ...] 'line_items' => [[ 'price_data' => [ 'currency' => 'usd', # To accept `grabpay`, all line items must have currency: `sgd`, `myr` 'currency' => 'sgd', 'product_data' => [ 'name' => 'T-shirt', ], 'unit_amount' => 2000, ], 'quantity' => 1, ]], 'mode' => 'payment', 'success_url' => 'https://example.com/success', 'cancel_url' => 'https://example.com/cancel', ]);
SessionCreateParams params = SessionCreateParams.builder() .addPaymentMethodType(SessionCreateParams.PaymentMethodType.CARD) .addPaymentMethodType(SessionCreateParams.PaymentMethodType.GRABPAY) .addLineItem( SessionCreateParams.LineItem.builder() .setPriceData( SessionCreateParams.LineItem.PriceData.builder() .setCurrency("usd") // To accept `grabpay`, all line items must have currency: `sgd`, `myr` .setCurrency("sgd") .setUnitAmount(2000L) .setProductData( SessionCreateParams.LineItem.PriceData.ProductData.builder() .setName("T-shirt") .build()) .build()) .setQuantity(1L) .build()) .setMode(SessionCreateParams.Mode.PAYMENT) .setSuccessUrl("https://example.com/success") .setCancelUrl("https://example.com/cancel") .build(); Session session = Session.create(params);
const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], payment_method_types: ['grabpay'], // or you can take multiple payment methods with // payment_method_types: ['card', 'grabpay', ...] line_items: [{ price_data: { currency: 'usd', // To accept `grabpay`, all line items must have currency: `sgd`, `myr` currency: 'sgd', product_data: { name: 'T-shirt', }, unit_amount: 2000, }, quantity: 1, }], mode: 'payment', success_url: 'https://example.com/success', cancel_url: 'https://example.com/cancel', });
params := &stripe.CheckoutSessionParams{ PaymentMethodTypes: stripe.StringSlice([]string{ "card", "grabpay", }), LineItems: []*stripe.CheckoutSessionLineItemParams{ &stripe.CheckoutSessionLineItemParams{ PriceData: &stripe.CheckoutSessionLineItemPriceDataParams{ Currency: stripe.String("usd"), // To accept `grabpay`, all line items must have currency: `sgd`, `myr` Currency: stripe.String("sgd"), ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: stripe.String("T-shirt"), }, UnitAmount: stripe.Int64(2000), }, Quantity: stripe.Int64(1), }, }, Mode: stripe.String(string(stripe.CheckoutSessionModePayment)), SuccessURL: stripe.String("https://example.com/success"), CancelURL: stripe.String("https://example.com/cancel"), } s, _ := session.New(params)
var options = new SessionCreateOptions { PaymentMethodTypes = new List<string> { "card", "grabpay", }, LineItems = new List<SessionLineItemOptions> { new SessionLineItemOptions { PriceData = new SessionLineItemPriceDataOptions { UnitAmount = 2000, Currency = "usd", // To accept `grabpay`, all line items must have currency: `sgd`, `myr` Currency = "sgd", ProductData = new SessionLineItemPriceDataProductDataOptions { Name = "T-shirt", }, }, Quantity = 1, }, }, Mode = "payment", SuccessUrl = "https://example.com/success", CancelUrl = "https://example.com/cancel", }; var service = new SessionService(); var session = service.Create(options);

Fulfill your orders

After accepting a payment, learn how to fulfill orders.

3 Test your integration

When testing your Checkout integration, select GrabPay as the payment method and click the Pay button.

GrabPay is a single-use payment method. Customers pay with GrabPay by redirecting from your iOS app to GrabPay to authorize the payment. After that, they will automatically be redirected back to your iOS app. You will get immediate notification on whether the payment succeeded or failed. For more details, see the section on how to fulfill the order.

Assets such as logos and payment buttons are provided in the branding guidelines section.

1 Set up Stripe Server-side Client-side

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

Client-side

The iOS SDK is open source, fully documented, and compatible with apps supporting iOS 11 or above.

  1. If you haven't already, install the latest version of CocoaPods.
  2. If you don't have an existing Podfile, run the following command to create one:
    pod init
  3. Add this line to your Podfile:
    pod 'Stripe'
  4. Run the following command:
    pod install
  5. Don't forget to use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file, from here on out.
  6. In the future, to update to the latest version of the SDK, just run:
    pod update Stripe
  1. If you haven't already, install the latest version of Carthage.
  2. Add this line to your Cartfile:
    github "stripe/stripe-ios"
  3. Follow the Carthage installation instructions.
  4. In the future, to update to the latest version of the SDK, run the following command:
    carthage update stripe-ios --platform ios
  1. Head to our GitHub releases page and download and unzip Stripe.framework.zip.
  2. Drag Stripe.framework to the "Embedded Binaries" section of your Xcode project's "General" settings. Make sure to select "Copy items if needed".
  3. Head to the "Build Phases" section of your Xcode project settings, and create a new "Run Script Build Phase". Paste the following snippet into the text field:
    bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Stripe.framework/integrate-dynamic-framework.sh"
  4. In the future, to update to the latest version of our SDK, just repeat steps 1 and 2.

When your app starts, configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API.

import UIKit import Stripe @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jx" // do any other necessary launch configuration return true } }
#import "AppDelegate.h" @import Stripe; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [StripeAPI setDefaultPublishableKey:@"pk_test_TYooMQauvdEDq54NiTphI7jx"]; // do any other necessary launch configuration return YES; } @end

2 Create a PaymentIntent Server-side Client-side

Server-side

Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.

Create a PaymentIntent on your server with an amount and currency. GrabPay currently only supports SGD in Singapore and MYR in Malaysia.

Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=grabpay \ -d amount=1099 \ -d currency=sgd
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' payment_intent = Stripe::PaymentIntent.create({ payment_method_types: ['grabpay'], amount: 1099, currency: 'sgd', })
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' payment_intent = stripe.PaymentIntent.create( payment_method_types=['grabpay'], amount=1099, currency='sgd', )
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $payment_intent = \Stripe\PaymentIntent::create([ 'payment_method_types' => ['grabpay'], 'amount' => 1099, 'currency' => 'sgd', ]);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("sgd") .addPaymentMethodType("grabpay") .build(); PaymentIntent paymentIntent = PaymentIntent.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ payment_method_types: ['grabpay'], amount: 1099, currency: 'sgd', });
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencySGD)), PaymentMethodTypes: stripe.StringSlice([]string{ "grabpay", }), } intent, _ := paymentintent.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var service = new PaymentIntentService(); var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "sgd", PaymentMethodTypes = new List<string> { "grabpay", }, }; var intent = service.Create(options);

Included in the returned PaymentIntent is a client secret, which is used on the client side to securely complete the payment process instead of passing the entire PaymentIntent object. There are different approaches that you can use to pass the client secret to the client side.

Client-side

On the client, request a PaymentIntent from your server and store its client secret.

class CheckoutViewController: UIViewController { var paymentIntentClientSecret: String? func startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
@interface CheckoutViewController () @property (strong) NSString *paymentIntentClientSecret; @end @implementation CheckoutViewController - (void)startCheckout { // Request a PaymentIntent from your server and store its client secret } @end

3 Redirect to the GrabPay Wallet Client-side

Set up a return URL

The iOS SDK presents a webview in your app to complete the GrabPay payment. When authentication is finished, the webview can automatically dismiss itself instead of having your customer close it. To enable this behavior, configure a custom URL scheme or universal link and set up your app delegate to forward the URL to the SDK.

// This method handles opening custom URL schemes (e.g., "your-app://") func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { guard let url = URLContexts.first?.url else { return } let stripeHandled = StripeAPI.handleURLCallback(with: url) if (!stripeHandled) { // This was not a Stripe url – handle the URL normally as you would } }
// This method handles opening custom URL schemes (e.g., "your-app://") - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { NSURL *url = URLContexts.allObjects.firstObject.URL; BOOL stripeHandled = [StripeAPI handleStripeURLCallbackWithURL:url]; if (!stripeHandled) { // This was not a stripe url – do whatever url handling your app // normally does, if any. } }
// This method handles opening custom URL schemes (e.g., "your-app://") func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } return false }
// This method handles opening custom URL schemes (e.g., "your-app://") - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { BOOL stripeHandled = [StripeAPI handleStripeURLCallbackWithURL:url]; if (stripeHandled) { return YES; } else { // This was not a Stripe url – handle the URL normally as you would } return NO; }

Confirm the payment

When the customer chooses to pay with GrabPay, complete the payment by calling the STPPaymentHandler confirmPayment method. This presents a webview where the customer completes payment on the GrabPay website.

class CheckoutViewController: UIViewController, STPAuthenticationContext { func pay() { let clientSecret = ... // The client secret of the PaymentIntent created in step 2 let grabPayParams = STPPaymentMethodGrabPayParams() let paymentMethodParams = STPPaymentMethodParams(grabPay: grabPayParams, billingDetails: nil, metadata: nil) let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret) paymentIntentParams.paymentMethodParams = paymentMethodParams paymentIntentParams.returnURL = "your-app://link" STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: self) { (handlerStatus, paymentIntent, error) in switch handlerStatus { case .succeeded: // Payment succeeded // ... case .canceled: // Payment canceled // ... case .failed: // Payment failed // ... @unknown default: fatalError() } } } func authenticationPresentingViewController() -> UIViewController { return self } }
@interface CheckoutViewController () <STPAuthenticationContext> @end @implementation CheckoutViewController - (void)pay { NSString *clientSecret = @""; // The client secret of the PaymentIntent created in step 2 STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret:clientSecret]; STPPaymentMethodGrabPayParams *grabPayParams = [[STPPaymentMethodGrabPayParams alloc] init]; paymentIntentParams.paymentMethodParams = [STPPaymentMethodParams paramsWithGrabPay:grabPayParams billingDetails:nil metadata:nil]; paymentIntentParams.returnURL = @"your-app://link"; [[STPPaymentHandler sharedHandler] confirmPayment:paymentIntentParams withAuthenticationContext:self completion:^(STPPaymentHandlerActionStatus handlerStatus, STPPaymentIntent * handledIntent, NSError * _Nullable handlerError) { switch (handlerStatus) { case STPPaymentHandlerActionStatusFailed: // Payment failed break; case STPPaymentHandlerActionStatusCanceled: // Payment canceled break; case STPPaymentHandlerActionStatusSucceeded: // Payment succeeded break; } }]; } # pragma mark STPAuthenticationContext - (UIViewController *)authenticationPresentingViewController { return self; } @end

4 Fulfill the order

We recommend using webhook events to confirm the charge has succeeded and to notify the customer that the payment is complete. The following events are sent when the PaymentIntent status is updated:

Event Description Next steps
payment_intent.succeeded The customer’s payment succeeded. Fulfill the goods or services that the customer purchased.
payment_intent.payment_failed The customer’s payment was declined. Contact the customer via email or push notification and request another payment method.

GrabPay is a single-use payment method. Customers pay with GrabPay by redirecting from your Android app to GrabPay to authorize the payment. After that, they will automatically be redirected back to your Android app. You will get immediate notification on whether the payment succeeded or failed. For more details, see the section on how to fulfill the order.

Assets such as logos and payment buttons are provided in the branding guidelines section.

1 Set up Stripe Client-side Server-side

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

Client-side

The Android SDK is open source and fully documented.

To install the SDK, add stripe-android to the dependencies block of your app/build.gradle file:

apply plugin: 'com.android.application' android { ... } dependencies { // ... // Stripe Android SDK implementation 'com.stripe:stripe-android:16.1.1' }

Configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API, such as in your Application subclass:

import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "pk_test_TYooMQauvdEDq54NiTphI7jx" ) } }
import com.stripe.android.PaymentConfiguration; public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); PaymentConfiguration.init( getApplicationContext(), "pk_test_TYooMQauvdEDq54NiTphI7jx" ); } }

Our code samples also use OkHttp and GSON to make HTTP requests to a server.

2 Create a PaymentIntent Server-side

A PaymentIntent is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage. First, create a PaymentIntent on your server and specify the amount to collect and the currency. If you already have an integration using the Payment Intents API, add grabpay to the list of payment method types for your PaymentIntent.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d "payment_method_types[]"=grabpay \ -d amount=1099 \ -d currency=sgd
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' payment_intent = Stripe::PaymentIntent.create({ payment_method_types: ['grabpay'], amount: 1099, currency: 'sgd', })
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' payment_intent = stripe.PaymentIntent.create( payment_method_types=['grabpay'], amount=1099, currency='sgd', )
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $payment_intent = \Stripe\PaymentIntent::create([ 'payment_method_types' => ['grabpay'], 'amount' => 1099, 'currency' => 'sgd', ]);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("sgd") .addPaymentMethodType("grabpay") .build(); PaymentIntent paymentIntent = PaymentIntent.create(params);
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ payment_method_types: ['grabpay'], amount: 1099, currency: 'sgd', });
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencySGD)), PaymentMethodTypes: stripe.StringSlice([]string{ "grabpay", }), } intent, _ := paymentintent.New(params)
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var service = new PaymentIntentService(); var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "sgd", PaymentMethodTypes = new List<string> { "grabpay", }, }; var intent = service.Create(options);

Included in the returned PaymentIntent is a client secret, which you’ll use to redirect the customer to their GrabPay wallet. Send the client secret back to the client so you can use it in the next step.

3 Pass the PaymentIntent client secret to the client Client-side

On the client, request a PaymentIntent from your server and store its client secret.

class GrabPayActivity : AppCompatActivity() { private lateinit var paymentIntentClientSecret: String override fun onCreate(savedInstanceState: Bundle?) { // ... fetchPaymentIntent() } private fun fetchPaymentIntent() { // Request a PaymentIntent from your server and store its client secret } }
public class GrabPayActivity extends AppCompatActivity { private String paymentIntentClientSecret; @Override public void onCreate(@Nullable Bundle savedInstanceState) { // ... fetchPaymentIntent(); } private void fetchPaymentIntent() { // Request a PaymentIntent from your server and store its client secret } }

4 Collect billing details Client-side

In your app, collect the optional billing details from the customer, e.g. customer name, and create a PaymentMethodCreateParams with these details.

private fun startCheckout() { // ... val billingDetails = PaymentMethod.BillingDetails(name = "Jenny Rosen") val paymentMethodCreateParams = PaymentMethodCreateParams.createGrabPay(billingDetails) }
private void startCheckout() { // ... PaymentMethod.BillingDetails billingDetails = new PaymentMethod.BillingDetails.Builder() .setName("Jenny Rosen") .build(); PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.createGrabPay(billingDetails); }

5 Submit the payment to Stripe Client-side

Retrieve the client secret from the PaymentIntent you created in Step 2 and call the stripe confirmPayment method. This presents a webview where the customer can complete the payment. Afterwards, onActivityResult is called with the result of the payment.

class GrabPayPaymentActivity : AppCompatActivity() { // ... private lateinit var paymentIntentClientSecret: String private val stripe: Stripe by lazy { Stripe( applicationContext, PaymentConfiguration.getInstance(applicationContext).publishableKey ) } private fun startCheckout() { // ... val confirmParams = ConfirmPaymentIntentParams .createWithPaymentMethodCreateParams( paymentMethodCreateParams = paymentMethodCreateParams, clientSecret = paymentIntentClientSecret, returnUrl = "yourapp://checkout_complete" ) stripe.confirmPayment(confirmParams) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // Handle the result of stripe.confirmPayment stripe.onPaymentResult(requestCode, data, object : ApiResultCallback<PaymentIntentResult> { override fun onSuccess(result: PaymentIntentResult) { val paymentIntent = result.intent val status = paymentIntent.status if (status == PaymentIntent.Status.Succeeded) { // Payment succeeded } else { // Payment failed/cancelled } } override fun onError(e: Exception) { // Payment failed } }) } }
public class GrabPayPaymentActivity extends AppCompatActivity { // ... private String paymentIntentClientSecret; private Stripe stripe; private void startCheckout() { // ... ConfirmPaymentIntentParams confirmParams = ConfirmPaymentIntentParams .createWithPaymentMethodCreateParams( paymentMethodCreateParams, paymentIntentClientSecret, "yourapp://checkout_complete" ); final Context context = getApplicationContext(); stripe = new Stripe( context, PaymentConfiguration.getInstance(context).getPublishableKey() ); stripe.confirmPayment(this, confirmParams); } // ... @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Handle the result of stripe.confirmPayment stripe.onPaymentResult(requestCode, data, new PaymentResultCallback()); } // ... private static final class PaymentResultCallback implements ApiResultCallback<PaymentIntentResult> { @Override public void onSuccess(@NonNull PaymentIntentResult result) { PaymentIntent paymentIntent = result.getIntent(); PaymentIntent.Status status = paymentIntent.getStatus(); if (status == PaymentIntent.Status.Succeeded) { // Payment succeeded } else { // Payment failed/cancelled } } @Override public void onError(@NonNull Exception e) { // Payment failed } } }

6 Fulfill the order

We recommend using webhook events to confirm the charge has succeeded and to notify the customer that the payment is complete. The following events are sent when the PaymentIntent status is updated:

Event Description Next steps
payment_intent.succeeded The customer’s payment succeeded. Fulfill the goods or services that the customer purchased.
payment_intent.payment_failed The customer’s payment was declined. Contact the customer via email or push notification and request another payment method.