Skip to content

Commit

Permalink
Merge pull request #656 from stripe/paymentsheet-ga
Browse files Browse the repository at this point in the history
PaymentSheet GA
  • Loading branch information
jaimepark-stripe authored Oct 19, 2021
2 parents bee27c7 + a5397bf commit cd323b4
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 25 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# CHANGELOG

## 0.2.3 - 2021-10-18

- [#565](https://github.com/stripe/stripe-react-native/pull/565) chore: Add jest mock file ([#565](https://github.com/stripe/stripe-react-native/issues/565))
- [#587](https://github.com/stripe/stripe-react-native/pull/587) chore: Update Podfile.lock stripe-react-native version ([#587](https://github.com/stripe/stripe-react-native/issues/587))
- [#568](https://github.com/stripe/stripe-react-native/pull/568) fix: check support for specific TextInputState methods ([#568](https://github.com/stripe/stripe-react-native/issues/568))
- [#631](https://github.com/stripe/stripe-react-native/pull/631) chore: Update tips migration guide ([#631](https://github.com/stripe/stripe-react-native/issues/631))
- [#601](https://github.com/stripe/stripe-react-native/pull/601) feat: Add button color, return URL, allowsDelayedPaymentMethods, and billing details to PaymentSheet ([#601](https://github.com/stripe/stripe-react-native/issues/601))

## 0.2.2 - 2021-09-15

- [#588](https://github.com/stripe/stripe-react-native/pull/588) fix: use the LocalBroadcastManager ([#588](https://github.com/stripe/stripe-react-native/issues/588))
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ Get started with our [📚 integration guides](https://stripe.com/docs/payments/

**Native UI**: We provide native screens and elements to securely collect payment details on Android and iOS.

**Pre-built payments UI (beta)**: [Learn how to integrate](https://stripe.com/docs/mobile/payments-ui-beta) Payment Sheet, our new pre-built payments UI for mobile apps. Our pre-built UI lets you accept cards, Apple Pay, and Google Pay out of the box, and includes support for saving & reusing cards. We'll be adding support for many more payment method during the beta.

**PaymentSheet**: [Learn how to integrate](https://stripe.com/docs/payments/accept-a-payment) PaymentSheet, our new pre-built payments UI for mobile apps. PaymentSheet lets you accept cards, Apple Pay, Google Pay, and much more out of the box and also supports saving & reusing payment methods. PaymentSheet currently accepts the following payment methods: Card, Apple Pay, Google Pay, SEPA Debit, Bancontact, iDEAL, and Sofort.
#### Recommended usage

If you're selling digital products or services within your app, (e.g. subscriptions, in-game currencies, game levels, access to premium content, or unlocking a full version), you must use the app store's in-app purchase APIs. See [Apple's](https://developer.apple.com/app-store/review/guidelines/#payments) and [Google's](https://support.google.com/googleplay/android-developer/answer/9858738?hl=en&ref_topic=9857752) guidelines for more information. For all other scenarios you can use this SDK to process payments via Stripe.
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ dependencies {
api 'com.facebook.react:react-native:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation 'com.stripe:stripe-android:17.1.0'
implementation 'com.stripe:stripe-android:18.1.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.reactnativestripesdk

import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
Expand Down Expand Up @@ -47,6 +48,9 @@ class PaymentSheetFragment : Fragment() {
val countryCode = arguments?.getString("merchantCountryCode").orEmpty()
val googlePayEnabled = arguments?.getBoolean("googlePay")
val testEnv = arguments?.getBoolean("testEnv")
val allowsDelayedPaymentMethods = arguments?.getBoolean("allowsDelayedPaymentMethods")
val primaryButtonColorHexStr = arguments?.getString("primaryButtonColor").orEmpty()
val billingDetailsBundle = arguments?.getBundle("defaultBillingDetails")
paymentIntentClientSecret = arguments?.getString("paymentIntentClientSecret").orEmpty()
setupIntentClientSecret = arguments?.getString("setupIntentClientSecret").orEmpty()

Expand Down Expand Up @@ -74,8 +78,31 @@ class PaymentSheetFragment : Fragment() {
}
}

var primaryButtonColor: ColorStateList? = null
if (primaryButtonColorHexStr != null && primaryButtonColorHexStr.isNotEmpty()) {
primaryButtonColor = ColorStateList.valueOf(Color.parseColor(primaryButtonColorHexStr))
}

var defaultBillingDetails: PaymentSheet.BillingDetails? = null
if (billingDetailsBundle != null) {
val addressBundle = billingDetailsBundle.getBundle("address")
val address = PaymentSheet.Address(addressBundle?.getString("city"),
addressBundle?.getString("country"),
addressBundle?.getString("line1"),
addressBundle?.getString("line2"),
addressBundle?.getString("postalCode"),
addressBundle?.getString("state"))
defaultBillingDetails = PaymentSheet.BillingDetails(address,
billingDetailsBundle?.getString("email"),
billingDetailsBundle?.getString("name"),
billingDetailsBundle?.getString("phone"))
}

paymentSheetConfiguration = PaymentSheet.Configuration(
merchantDisplayName = merchantDisplayName,
allowsDelayedPaymentMethods = allowsDelayedPaymentMethods ?: false,
primaryButtonColor = primaryButtonColor,
defaultBillingDetails=defaultBillingDetails,
customer = if (customerId.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) PaymentSheet.CustomerConfiguration(
id = customerId,
ephemeralKeySecret = customerEphemeralKeySecret
Expand Down
28 changes: 17 additions & 11 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,19 @@ PODS:
- React-Core
- RNScreens (2.15.0):
- React-Core
- Stripe (21.8.1):
- Stripe/Stripe3DS2 (= 21.8.1)
- StripeCore (= 21.8.1)
- Stripe (21.9.0):
- Stripe/Stripe3DS2 (= 21.9.0)
- StripeCore (= 21.9.0)
- StripeUICore (= 21.9.0)
- stripe-react-native (0.2.2):
- React-Core
- Stripe (~> 21.8.1)
- Stripe/Stripe3DS2 (21.8.1):
- StripeCore (= 21.8.1)
- StripeCore (21.8.1)
- Stripe (~> 21.9.0)
- Stripe/Stripe3DS2 (21.9.0):
- StripeCore (= 21.9.0)
- StripeUICore (= 21.9.0)
- StripeCore (21.9.0)
- StripeUICore (21.9.0):
- StripeCore (= 21.9.0)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -315,6 +319,7 @@ SPEC REPOS:
- boost-for-react-native
- Stripe
- StripeCore
- StripeUICore

EXTERNAL SOURCES:
DoubleConversion:
Expand Down Expand Up @@ -420,11 +425,12 @@ SPEC CHECKSUMS:
RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39
RNReanimated: e03f7425cb7a38dcf1b644d680d1bfc91c3337ad
RNScreens: 2ad555d4d9fa10b91bb765ca07fe9b29d59573f0
Stripe: 57c6997c64042a3f5aedae9b76e331942f8b75d2
stripe-react-native: 8df9699a6788463d2a0febf481b429a1974321a4
StripeCore: a6e50d58119a0c7601773b56f274db2d394dcdac
Stripe: 41c3d261501e1dc84755b1bdabdaae50ebf92b53
stripe-react-native: 5ada54a0985880a76fd1bec0d9434a70b04f5d62
StripeCore: 2ea9531e863ef20f191a0d61f00dedb7f061baef
StripeUICore: a0f9e40520823d34c63baec0782fc4a0c7fb7484
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6

PODFILE CHECKSUM: 32acfd4fec652a6f224d467187287782155cfbdc

COCOAPODS: 1.10.1
COCOAPODS: 1.10.2
2 changes: 2 additions & 0 deletions example/ios/StripeSdkExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,15 @@
"${PODS_CONFIGURATION_BUILD_DIR}/Stripe/Stripe.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/Stripe/Stripe3DS2.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/StripeCore/StripeCore.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/StripeUICore/StripeUICore.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Stripe.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Stripe3DS2.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/StripeCore.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/StripeUICore.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down
53 changes: 46 additions & 7 deletions example/src/screens/PaymentsUICompleteScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { useState } from 'react';
import { Alert } from 'react-native';
import { useStripe } from '@stripe/stripe-react-native';
import {
useStripe,
PaymentSheet,
PaymentSheetError,
} from '@stripe/stripe-react-native';
import Button from '../components/Button';
import PaymentScreen from '../components/PaymentScreen';
import { API_URL } from '../Config';
Expand Down Expand Up @@ -34,10 +38,18 @@ export default function PaymentsUICompleteScreen() {
setLoadng(true);
const { error } = await presentPaymentSheet();

if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else {
if (!error) {
Alert.alert('Success', 'The payment was confirmed successfully');
} else if (error.code === PaymentSheetError.Failed) {
Alert.alert(
`PaymentSheet present failed with error code: ${error.code}`,
error.message
);
} else if (error.code === PaymentSheetError.Canceled) {
Alert.alert(
`PaymentSheet present was canceled with code: ${error.code}`,
error.message
);
}
setPaymentSheetEnabled(false);
setLoadng(false);
Expand All @@ -47,6 +59,21 @@ export default function PaymentsUICompleteScreen() {
const { paymentIntent, ephemeralKey, customer } =
await fetchPaymentSheetParams();

const address: PaymentSheet.Address = {
city: 'San Francisco',
country: 'AT',
line1: '510 Townsend St.',
line2: '123 Street',
postalCode: '94102',
state: 'California',
};
const billingDetails: PaymentSheet.BillingDetails = {
name: 'Jane Doe',
email: '[email protected]',
phone: '555-555-555',
address: address,
};

const { error } = await initPaymentSheet({
customerId: customer,
customerEphemeralKeySecret: ephemeralKey,
Expand All @@ -55,14 +82,26 @@ export default function PaymentsUICompleteScreen() {
merchantDisplayName: 'Example Inc.',
applePay: true,
merchantCountryCode: 'US',
style: 'alwaysDark',
style: 'automatic',
googlePay: true,
testEnv: true,
primaryButtonColor: '#635BFF', // Blurple
returnURL: 'stripe-example://stripe-redirect',
defaultBillingDetails: billingDetails,
allowsDelayedPaymentMethods: true,
});
if (!error) {
setPaymentSheetEnabled(true);
} else {
Alert.alert(`Error code: ${error.code}`, error.message);
} else if (error.code === PaymentSheetError.Failed) {
Alert.alert(
`PaymentSheet init failed with error code: ${error.code}`,
error.message
);
} else if (error.code === PaymentSheetError.Canceled) {
Alert.alert(
`PaymentSheet init was canceled with code: ${error.code}`,
error.message
);
}
};

Expand Down
22 changes: 20 additions & 2 deletions example/src/screens/PaymentsUICustomScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Alert, Image, StyleSheet, Text, View } from 'react-native';
import { useStripe } from '@stripe/stripe-react-native';
import { useStripe, PaymentSheet } from '@stripe/stripe-react-native';
import { colors } from '../colors';
import Button from '../components/Button';
import PaymentScreen from '../components/PaymentScreen';
Expand Down Expand Up @@ -39,6 +39,21 @@ export default function PaymentsUICustomScreen() {
const { paymentIntent, ephemeralKey, customer } =
await fetchPaymentSheetParams();

const address: PaymentSheet.Address = {
city: 'San Francisco',
country: 'AT',
line1: '510 Townsend St.',
line2: '123 Street',
postalCode: '94102',
state: 'California',
};
const billingDetails: PaymentSheet.BillingDetails = {
name: 'Jane Doe',
email: '[email protected]',
phone: '555-555-555',
address: address,
};

const { error, paymentOption } = await initPaymentSheet({
customerId: customer,
customerEphemeralKeySecret: ephemeralKey,
Expand All @@ -47,9 +62,12 @@ export default function PaymentsUICustomScreen() {
merchantDisplayName: 'Example Inc.',
applePay: true,
merchantCountryCode: 'US',
style: 'alwaysDark',
style: 'automatic',
googlePay: true,
testEnv: true,
primaryButtonColor: '#635BFF', // Blurple
returnURL: 'stripe-example://stripe-redirect',
defaultBillingDetails: billingDetails,
});

if (!error) {
Expand Down
29 changes: 29 additions & 0 deletions ios/StripeSdk.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,35 @@ class StripeSdk: RCTEventEmitter, STPApplePayContextDelegate, STPBankSelectionVi
configuration.merchantDisplayName = merchantDisplayName
}

if let returnURL = params["returnURL"] as? String {
configuration.returnURL = returnURL
}

if let buttonColorHexStr = params["primaryButtonColor"] as? String {
let primaryButtonColor = UIColor(hexString: buttonColorHexStr)
configuration.primaryButtonColor = primaryButtonColor
}

if let allowsDelayedPaymentMethods = params["allowsDelayedPaymentMethods"] as? Bool {
configuration.allowsDelayedPaymentMethods = allowsDelayedPaymentMethods
}

if let defaultBillingDetails = params["defaultBillingDetails"] as? [String: Any?] {
configuration.defaultBillingDetails.name = defaultBillingDetails["name"] as? String
configuration.defaultBillingDetails.email = defaultBillingDetails["email"] as? String
configuration.defaultBillingDetails.phone = defaultBillingDetails["phone"] as? String

if let address = defaultBillingDetails["address"] as? [String: String] {
configuration.defaultBillingDetails.address = .init(city: address["city"],
country: address["country"],
line1: address["line1"],
line2: address["line2"],
postalCode: address["postalCode"],
state: address["state"])
}

}

if let customerId = params["customerId"] as? String {
if let customerEphemeralKeySecret = params["customerEphemeralKeySecret"] as? String {
if (!Errors.isEKClientSecretValid(clientSecret: customerEphemeralKeySecret)) {
Expand Down
19 changes: 18 additions & 1 deletion src/types/PaymentSheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export declare namespace PaymentSheet {
customFlow?: boolean;
merchantDisplayName?: string;
style?: 'alwaysLight' | 'alwaysDark' | 'automatic';
returnURL?: string;
primaryButtonColor?: string;
defaultBillingDetails?: BillingDetails;
allowsDelayedPaymentMethods?: boolean;
};

type ClientSecretParams =
Expand Down Expand Up @@ -40,7 +44,20 @@ export declare namespace PaymentSheet {
merchantCountryCode?: string;
testEnv?: boolean;
};

export interface Address {
city?: string;
country?: string;
line1?: string;
line2?: string;
postalCode?: string;
state?: string;
}
export interface BillingDetails {
address: Address;
name?: string;
email?: string;
phone?: string;
}
export interface PaymentOption {
label: string;
image: string;
Expand Down
2 changes: 1 addition & 1 deletion stripe-react-native.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ Pod::Spec.new do |s|


s.dependency "React-Core"
s.dependency 'Stripe', '~> 21.8.1'
s.dependency 'Stripe', '~> 21.9.0'
end

0 comments on commit cd323b4

Please sign in to comment.