Skip to content

Commit

Permalink
Android Pay Support (#17)
Browse files Browse the repository at this point in the history
* Add Android Pay support

* Update docs

* Add Android Pay to lib

* Update example

* Update yarn.lock files

* Update demo build
  • Loading branch information
naoufal authored Sep 7, 2017
1 parent 56136da commit 5b45f79
Show file tree
Hide file tree
Showing 24 changed files with 952 additions and 243 deletions.
148 changes: 132 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@
[![npm](https://img.shields.io/npm/dm/react-native-payments.svg?style=flat-square)](https://www.npmjs.com/package/react-native-payments)
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)

Accept Payments with Apple Pay using the [Payment Request API](https://paymentrequest.show).
Accept Payments with Apple Pay and Android Pay using the [Payment Request API](https://paymentrequest.show).

__Features__
- __Simple.__ No more checkout forms.
- __Effective__. Faster checkouts that increase conversion.
- __Future-proof__. Use a W3C Standards API, supported by companies like Google, Firefox and others.
- __Cross-platform__. Share payments code between your iOS and web apps.
- __Cross-platform__. Share payments code between your iOS, Android, and web apps.
- __Add-ons__. Easily enable support for Stripe or Braintree via add-ons.

<div>
<img width="280px" src="https://user-images.githubusercontent.com/1627824/27758096-9fc6bf9a-5dc1-11e7-9d8f-b2d409302fc7.gif" />
<img width="280px" src="https://user-images.githubusercontent.com/1627824/30039983-d75d1b3e-91d8-11e7-9ac9-71d2ed12958c.png" />
</div>

---

Expand Down Expand Up @@ -50,7 +53,7 @@ $ react-native link react-native-payments
```

## Usage
- [Registering as a Merchant](#registering-as-a-merchant)
- [Setting up Apple Pay/Android Pay](#setting-up-apple-payandroid-pay)
- [Importing the Library](#importing-the-library)
- [Initializing the Payment Request](#initializing-the-payment-request)
- [Displaying the Payment Request](#displaying-the-payment-request)
Expand All @@ -61,17 +64,25 @@ $ react-native link react-native-payments
- [Dismissing the Payment Request](#dismissing-the-payment-request)


### Registering as a Merchant
Before you can start accepting payments with Apple Pay, there are a few steps you'll need to go through:
### Setting up Apple Pay/Android Pay
Before you can start accepting payments in your App, you'll need to setup Apple Pay and/or Android Pay.

#### Apple Pay
1. Register as an Apple Developer
1. Obtain a merchant ID
2. Enable Apple Pay in your app
1. Enable Apple Pay in your app

Apple has a documentation on how to do both of these in their _[Configuring your Environment](https://developer.apple.com/library/content/ApplePay_Guide/Configuration.html)_ guide.
Apple has a documentation on how to do this in their _[Configuring your Environment](https://developer.apple.com/library/content/ApplePay_Guide/Configuration.html)_ guide.

#### Android Pay

1. Add Android Pay and Google Play Services to your dependencies
1. Enable Android Pay in your Manifest

Google has documentation on how to do this in their _[Setup Android Pay](https://developers.google.com/android-pay/setup)_ guide.

### Importing the Library
Once Apple Pay is enabled in your app, jump into your app's entrypoint and make the `PaymentRequest` globally available to your app.
Once Apple Pay/Android Pay is enabled in your app, jump into your app's entrypoint and make the `PaymentRequest` globally available to your app.

```es6
// index.ios.js
Expand All @@ -96,6 +107,29 @@ const METHOD_DATA = [{
}];
```

<details>
<summary><strong>See Android Pay Example</strong></summary>
<br/>

```es6
const METHOD_DATA = [{
supportedMethods: ['android-pay'],
data: {
supportedNetworks: ['visa', 'mastercard', 'amex'],
currencyCode: 'USD',
environment: 'TEST', // defaults to production
paymentMethodTokenizationParameters: {
tokenizationType: 'NETWORK_TOKEN',
parameters: {
publicKey: 'your-pubic-key'
}
}
}
}];
```

</details>

#### Payment Details
Payment Details is where define transaction details like display items, a total and optionally shipping options.

Expand Down Expand Up @@ -123,6 +157,8 @@ Once you've defined your `methodData` and `details`, you're ready to initialize
const paymentRequest = new PaymentRequest(METHOD_DATA, DETAILS);
```

🚨 _Note: On Android, display items are not displayed within the Android Pay view. Instead, the _[User Flows documentation](https://developers.google.com/android-pay/payment-flows)_ suggests showing users a confirmation view where you list the display items. When using React Native Payments, show this view after receiving the `PaymentResponse`._

### Displaying the Payment Request
Now that you've setup your Payment Request, displaying it is as simple as calling the `show` method.

Expand All @@ -131,9 +167,10 @@ paymentRequest.show();
```

<details>
<summary><strong>See Screenshot</strong></summary>
<summary><strong>See Screenshots</strong></summary>
<br/>
<img width="250px" src="https://user-images.githubusercontent.com/1627824/27548765-be9121c0-5a4e-11e7-8e45-4b460e314e6a.png" />
<img width="250px" src="https://user-images.githubusercontent.com/1627824/30039982-d565c68c-91d8-11e7-9cb9-2a0e58fffe6a.png" />

</details>

Expand All @@ -144,6 +181,8 @@ You can abort the Payment Request at any point by calling the `abort` method.
paymentRequest.abort();
```

🚨 _Note: Not yet implemented on Android Pay_

### Requesting Contact Information
Some apps may require contact information from a user. You can do so by providing a [`PaymentOptions`]() as a third argument when initializing a Payment Request. Using Payment Options, you can request a contact name, phone number and/or email.

Expand All @@ -157,12 +196,15 @@ const OPTIONS = {
```

<details>
<summary><strong>See Screenshot</strong></summary>
<summary><strong>See Screenshots</strong></summary>
<br/>
<img width="250px" src="https://user-images.githubusercontent.com/1627824/27549933-9be15be6-5a52-11e7-868a-abcfc8867968.png" />
<img width="250px" src="https://user-images.githubusercontent.com/1627824/30039983-d75d1b3e-91d8-11e7-9ac9-71d2ed12958c.png" />

</details>
<br/>

🚨 _Note: On Android, requesting a contact name will present the user with a shipping address selector. If you're not shipping anything to the user, consider capturing the contact name outside of Android Pay._

#### Requesting a Phone Number
Set `requestPayerPhone` to `true` to request a phone number.
Expand All @@ -174,11 +216,15 @@ const OPTIONS = {
```

<details>
<summary><strong>See Screenshot</strong></summary>
<summary><strong>See Screenshots</strong></summary>
<br/>
<img width="250px" src="https://user-images.githubusercontent.com/1627824/27549958-b6732160-5a52-11e7-8813-3beeeed03b9c.png" />
<img width="250px" src="https://user-images.githubusercontent.com/1627824/30039983-d75d1b3e-91d8-11e7-9ac9-71d2ed12958c.png" />

</details>
<br/>

🚨 _Note: On Android, requesting a phone number will present the user with a shipping address selector. If you're not shipping anything to the user, consider capturing the phone number outside of Android Pay._

#### Requesting an Email Address
Set `requestPayerEmail` to `true` to request an email address.
Expand All @@ -190,9 +236,10 @@ const OPTIONS = {
```

<details>
<summary><strong>See Screenshot</strong></summary>
<summary><strong>See Screenshots</strong></summary>
<br/>
<img width="250px" src="https://user-images.githubusercontent.com/1627824/27549968-c172ac34-5a52-11e7-973d-8d06a3beb5ce.png" />
<img width="250px" src="https://user-images.githubusercontent.com/1627824/30039982-d565c68c-91d8-11e7-9cb9-2a0e58fffe6a.png" />

</details>
<br/>
Expand Down Expand Up @@ -251,7 +298,7 @@ paymentRequest.addEventListener('shippingaddresschange', e => {
e.updateWith(updatedDetails);
});

paymentRequest.addEventListener('shippingaddresschange', e => {
paymentRequest.addEventListener('shippingoptionchange', e => {
const updatedDetails = getUpdatedDetailsForShippingOption(paymentRequest.shippingOption);

e.updateWith(updatedDetails);
Expand All @@ -260,6 +307,8 @@ paymentRequest.addEventListener('shippingaddresschange', e => {
For a deeper dive on handling shipping in Payment Request, checkout Google's _[Shipping in Payment Request](https://developers.google.com/web/fundamentals/discovery-and-monetization/payment-request/deep-dive-into-payment-request#shipping_in_payment_request_api)_.
🚨 _Note: On Android, there are no `shippingaddresschange` and `shippingoptionchange` events. To allow users to update their shipping address, you'll need to trigger a new `PaymentRequest`. Updating shipping options typically happens after the receiving the `PaymentResponse` and before calling its `getPaymentToken` method._
### Processing Payments
Now that we know how to initialize, display, and dismiss a Payment Request, let's take a look at how to process payments.
Expand All @@ -274,10 +323,10 @@ paymentRequest.show()
});
```
There are two ways to process Apple Pay payments -- on your server or using a payment processor.
There are two ways to process Apple Pay/Android Pay payments -- on your server or using a payment processor.
#### Processing Payments on Your Server
If you're equiped to Apple Pay payments on your server, all you have to do is send the Payment Response's `transactionIdentifier` and `paymentData` to your server.
If you're equiped to process Apple Pay/Android Pay payments on your server, all you have to do is send the Payment Response data to your server.
```es6
import { NativeModules } from 'react-native';
Expand All @@ -299,6 +348,37 @@ paymentRequest.show()
});
```
<details>
<summary><strong>See Android Pay Example</strong></summary>
<br/>
```es6
paymentRequest.show()
.then(paymentResponse => {
const { getPaymentToken } = paymentResponse.details;

return getPaymentToken()
.then(paymentToken => {
const { ephemeralPublicKey, encryptedMessage, tag } = paymentResponse.details;

return fetch('...', {
method: 'POST',
body: {
ephemeralPublicKey,
encryptedMessage,
tag
}
})
.then(res => res.json())
.then(successHandler)
.catch(errorHandler)
});
});
```
</details>
<br/>
You can learn more about server-side decrypting of Payment Tokens on Apple's [Payment Token Format Reference](https://developer.apple.com/library/content/documentation/PassKit/Reference/PaymentTokenJSON/PaymentTokenJSON.html) documentation.
#### Processing Payments with a Payment Processor
Expand All @@ -307,7 +387,7 @@ When using a payment processor, you'll receive a `paymentToken` field within the
```es6
paymentRequest.show()
.then(paymentResponse => {
const { paymentToken } = paymentResponse.details;
const { paymentToken } = paymentResponse.details; // On Android, you need to invoke the `getPaymentToken` method to receive the `paymentToken`.

return fetch('...', {
method: 'POST',
Expand All @@ -321,6 +401,32 @@ paymentRequest.show()
});
```
<details>
<summary><strong>See Android Pay Example</strong></summary>
<br/>
```es6
paymentRequest.show()
.then(paymentResponse => {
const { getPaymentToken } = paymentResponse.details;

return getPaymentToken()
.then(paymentToken => fetch('...', {
method: 'POST',
body: {
paymentToken
}
})
.then(res => res.json())
.then(successHandler)
.catch(errorHandler);
});
});
```
</details>
<br/>
For a list of supported payment processors and how to enable them, see the [Add-ons](#add-ons) section.
### Dismissing the Payment Request
Expand All @@ -330,11 +436,15 @@ Dismissing the Payment Request is as simple as calling the `complete` method on
paymentResponse.complete('success'); // Alternatively, you can call it with `fail` or `unknown`
```
🚨 _Note: On Android, there is no need to call `paymentResponse.complete` -- the PaymentRequest dismisses itself._
## Add-ons
Here's a list of Payment Processors that you can enable via add-ons:
- [Stripe](https://github.com/naoufal/react-native-payments/blob/master/packages/react-native-payments-addon-stripe)
- [Braintree](https://github.com/naoufal/react-native-payments/blob/master/cli/packages/react-native-payments-addon-braintree)
🚨 _Note: On Android, Payment Processors are enabled by default._
## API
### [PaymentRequest](https://github.com/naoufal/react-native-payments/blob/master/cli/packages/react-native-payments/docs/PaymentRequest.md)
### [PaymentRequestUpdateEvent](https://github.com/naoufal/react-native-payments/blob/master/cli/packages/react-native-payments/docs/PaymentRequestUpdateEvent.md)
Expand All @@ -354,6 +464,12 @@ Here's a list of Payment Processors that you can enable via add-ons:
- [Processing Payments](https://developer.apple.com/library/content/ApplePay_Guide/ProcessPayment.html#//apple_ref/doc/uid/TP40014764-CH5-SW4)
- [Payment Token Format Reference](https://developer.apple.com/library/content/documentation/PassKit/Reference/PaymentTokenJSON/PaymentTokenJSON.html#//apple_ref/doc/uid/TP40014929)
### Android Pay
- [Setup Android Pay](https://developers.google.com/android-pay/setup)
- [User Flows](https://developers.google.com/android-pay/payment-flows)
- [Best Practices](https://developers.google.com/android-pay/best-practices)
- [Gateway Token Approach](https://developers.google.com/web/fundamentals/discovery-and-monetization/payment-request/android-pay#gateway_token_approach)
- [Network Token Approach](https://developers.google.com/web/fundamentals/discovery-and-monetization/payment-request/android-pay#network_token_approach)
# License
Licensed under the MIT License, Copyright © 2017, [Naoufal Kadhom](https://twitter.com/naoufal).
Expand Down
3 changes: 2 additions & 1 deletion packages/react-native-payments-addon-stripe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const METHOD_DATA = [{
+ paymentMethodTokenizationParameters: {
+ parameters: {
+ gateway: 'stripe',
+ 'stripe:publishableKey': 'your_publishable_key'
+ 'stripe:publishableKey': 'your_publishable_key',
+ 'stripe:version': '5.0.0' // Only required on Android
+ }
+ }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@

buildscript {
repositories {
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}

apply plugin: 'com.android.library'

android {
Expand All @@ -20,17 +9,17 @@ android {
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
lintOptions {
abortOnError false
warning 'InvalidPackage'
}
}

repositories {
mavenCentral()
}

dependencies {
compile 'com.facebook.react:react-native:+'
compile 'com.google.android.gms:play-services-wallet:11.0.4'
compile 'com.android.support:support-v4:23.0.1'
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactlibrary">
package="com.reactnativepayments">

</manifest>

Loading

0 comments on commit 5b45f79

Please sign in to comment.