Google Pay and Apple pay using Stripe on Flutter
Tags: flutter, applepay, googlepay, stripe
Published: 2021-05-29 19:12:51

After lots of switching packages, configuring gradles, installing pods to make it work. I am finally happy to see google putting an end to this battle with the introduction of pay library. As the name is, its really simple and straight forward, yet I felt there must be some resource  out on the internet to help devs to implement such a feature in short time so that you can focus on developing something  really awesome.

I am making it very simple where you can just copy paste bunch of lines below and make it work.

First off all you need the following
* Stripe account and publishable key
* Enable apple pay on stripe and upload certificate generated from [here](https://developer.apple.com/account/resources/certificates/list)
* Google merchant account from [here](https://pay.google.com/business)


Now lets start, 

## Configuration

### Apple Pay

And the following json file as your assets file inside `/assets/apple_pay.json` folder
```
{
  "provider": "apple_pay",
  "data": {
    "merchantIdentifier": "<Merchant ID from developer.apple.com>",
    "displayName": "<Display Name>",
    "merchantCapabilities": [
      "3DS",
      "debit",
      "credit"
    ],
    "supportedNetworks": [
      "amex",
      "visa",
      "discover",
      "masterCard"
    ],
    "countryCode": "FR", // Country code
    "currencyCode": "EUR", // Currency code
    "requiredBillingContactFields": null, 
    "requiredShippingContactFields": null
  }
}
```

### Google Pay

And the following json file as your assets file inside `/assets/google_pay.json` folder

```
{
  "provider": "google_pay",
  "data": {
    "environment": "TEST",
    "apiVersion": 2,
    "apiVersionMinor": 0,
    "allowedPaymentMethods": [
      {
        "type": "CARD",
        "tokenizationSpecification": {
          "type": "PAYMENT_GATEWAY",
          "parameters": {
            "gateway": "stripe",
            "stripe:version": "2020-08-27",
            "stripe:publishableKey": "<stripe publishable key, eg pk_.......>"
          }
        },
        "parameters": {
          "allowedCardNetworks": [
            "VISA",
            "MASTERCARD"
          ],
          "allowedAuthMethods": [
            "PAN_ONLY",
            "CRYPTOGRAM_3DS"
          ],
          "billingAddressRequired": false
        }
      }
    ],
    "merchantInfo": {
      "merchantId": "<Merchant ID from pay.google.com/business>",
      "merchantName": "<Display Name>"
    },
    "transactionInfo": {
      "countryCode": "FR",
      "currencyCode": "EUR"
    }
  }
}
```


# Flutter Code

## Install Package

Inside your `pubspec.yaml` file add

```
pay: 1.0.3 # Latest one at the time of writing
``` 

## Initialize the package

Somewhere on your code base first enable the package with the above json files from assets that you just added.

```
Pay _pay = Pay.withAssets(["google_pay.json","apple_pay.json"]);
```

## Check the availability 
Once you enable the package, now you can check the availability of the payment service. I link to maintain an array of providers that are available, to do so.

```
    List<PayProvider> availableProviders = [];
    PayProvider.values.forEach((e) async {
      bool canPay = await _pay.userCanPay(e);
      if (canPay) availableProviders.add(e);
    });
```

## Show Apple Pay buttons

### Option 1 (The quickest) (Either this one or the other one, I prefer the 2nd one)

You can use provided native pay button that can directly process payments by writing everything on the widget to start making the payment happen.

1. Google Pay Button
```
  if(availableProviders.contains(PayProvider.google_pay))
    GooglePayButton(
      paymentConfigurationAsset: "google_pay.json",
      onPaymentResult: (Map data){
        print(data);
      },
      paymentItems: [ // You can have multiple items on your payment
        PaymentItem(
          amount: "100", // Your amount
          label: "AwesomeProduct", // Your amount title
          status: pay.PaymentItemStatus.final_price, // Status of the payment
          type: pay.PaymentItemType.total, // Type of the payment
        )
      ],
    )
```

2. Apple Pay Button
```
  if(availableProviders.contains(PayProvider.apple_pay))
    ApplePayButton(
      paymentConfigurationAsset: "apple_pay.json",
      onPaymentResult: (Map data){
        print(data);
      },
      paymentItems: [ // You can have multiple items on your payment
        PaymentItem(
          amount: "100", // Your amount
          label: "AwesomeProduct", // Your amount title
          status: pay.PaymentItemStatus.final_price, // Status of the payment
          type: pay.PaymentItemType.total, // Type of the payment
        )
      ],
    )
```


### Option 2 (The best, IMO)

If you want to separate business code and the UI code, this is the one for you. Instead of using the provided widget you can use any widget (Make sure you comply with button guidlies from [Apple Pay](https://developer.apple.com/design/human-interface-guidelines/apple-pay/overview/buttons-and-marks/) or [Google Pay](https://developers.google.com/pay/api/web/guides/brand-guidelines))

```
    Map<String, dynamic> data = await _pay.showPaymentSelector(
      provider: provider // Can be supported provider (PayProvider.apple_pay, PayProvider.google_pay),
      paymentItems: [
        PaymentItem(
          label: "Awesome Product",
          amount: "100",
          type: PaymentItemType.item,
          status: PaymentItemStatus.final_price,
        ),
      ],
    );
```

After any of the option, you will get a response with a `Map data`. The reason it's a Map and not a typed value is every provider returns response on their own pattern so to support multiple providers, pay packages is making to map to make the package as raw as possible.

From the response, all you need is token which should be as follows

```
String tokenToBeSentToCloud = data["token"];
```

## Server Side
Making actual charge to the user's card

After you have a token, you now need to have a backend code(can also be on on flutter, not recommended) to process the payment. I am using a node.js server to process the payment using the token from mobile.

### Install the package
`npm install stripe` or `yarn add stripe`

### Initialize the package
```
export const stripe = new Stripe("<stripe_secret_key>", {
    apiVersion: "2020-08-27"
});
```


### Make a charge



```
    const paymentMethod = await stripe.paymentMethods.create({
        type: "card",
        card: {
            token: "<token from pay package>"
        }
    })

    await stripe.paymentIntents.create({
        amount: 10000, // Amount in cents, can be different from what you presented on the pay dialog on mobiel
        currency: "EUR", // Currentcy
        capture_method: "automatic", // 
        customer: "<Optional, if you have customer on stripe>",
        payment_method: paymentMethod.id,
        receipt_email: "<If you want to send reciept in email>,
        statement_descriptor: "<Statement Label(max 22 chars)>",
        confirm: true, // True charges the card instantly
        description: "Product description"
    })
```

Voila, you just charges a user.

Now there are other cool things that you can do with stripe.js SDK, for that checkout stripe [documentation](https://stripe.com/docs/js) for more features.