Brand configurations

This section contains guidelines for specific brands.

Apple Pay

Apple Pay is easy to set up. It gives customers a simple and secure way to pay using an iPhone or iPad. This guide explains how to process Apple Pay payments with the SDK.

Configuration

To use Apple Pay on a real device, create keys and a certificate.

  1. Add an Apple Pay merchant ID in Apple Developer Centre.
  2. In Smart Payments Platform, go to Administration -> Mobile Payment, select Generate keys, and download the CSR (certificate signing request).
  3. In Apple Developer Centre, select the merchant ID and choose Create Certificate. Upload the CSR from step 2, and download the certificate. See the Apple Pay guide for details.
  4. Upload the certificate in Smart Payments Platform.
  5. Turn on Apple Pay in Xcode: enable Apple Pay under Capabilities in Project Settings, then select the merchant ID the app should use.

Add Apple Pay to the app using one of two methods: Ready-to-Use UI or SDK & Your Own UI. Follow the relevant instructions below.

Ready-to-Use UI

  1. Add PKPaymentButton to the view.
  2. Add a button action method to the view controller. The code sample below shows the main steps.
- (IBAction) applePayTapped:(id)sender {
    // request checkout ID from your server
    // configure OPPCheckoutProvider
    // present checkout for specific payment brand and handle callbacks
}
@IBAction func applePayTapped(_ sender: Any) {    
    // request checkout ID from your server
    // configure OPPCheckoutProvider
    // present checkout for specific payment brand and implement callbacks
}

Request checkout ID

To start a payment, create a checkout ID. The app should request a checkout ID from the server. This example uses a sample integration server; adapt it to use a backend API.

NSURLRequest *merchantServerRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://YOUR_URL/?amount=100&currency=EUR&paymentType=DB"]];
[[[NSURLSession sharedSession] dataTaskWithRequest:merchantServerRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    // Ensure that you handle errors
    NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    self.checkoutID = JSON[@"checkoutId"];
}] resume];
let merchantServerRequest = NSURLRequest(url: URL(string: "https://YOUR_URL/?amount=100&currency=EUR&paymentType=DB")!)
URLSession.shared.dataTask(with: merchantServerRequest as URLRequest) { (data, response, error) in
    // Ensure that you handle errors
    if let json = try? JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any] {
        let checkoutID = json?["checkoutId"] as? String
    }
}.resume()

Create and configure checkout provider

Create a PKPaymentRequest object and add it to OPPCheckoutSettings.

Use OPPPaymentProvider to construct a PKPaymentRequest with:

  • merchantIdentifier
  • countryCode
  • Default values:
    • supportedNetworks includes all payment networks
    • merchantCapabilities includes threeDSecure

If not using the helper method, ensure merchantCapabilities includes threeDSecure. The emv value is not supported.

OPPPaymentProvider *provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeTest];

OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
PKPaymentRequest *paymentRequest = [OPPPaymentProvider paymentRequestWithMerchantIdentifier:@"YOUR MERCHANT ID" countryCode:@"YOUR MERCHANT COUNTRY CODE"];
paymentRequest.supportedNetworks = ... // set up supported payment networks
checkoutSettings.applePayPaymentRequest = paymentRequest;
OPPCheckoutProvider *checkoutProvider = [OPPCheckoutProvider checkoutProviderWithPaymentProvider:provider
                                                                                      checkoutID:checkoutID
                                                                                        settings:checkoutSettings];
let provider = OPPPaymentProvider(mode: OPPProviderMode.test)

let checkoutSettings = OPPCheckoutSettings()
let paymentRequest = OPPPaymentProvider.paymentRequest(withMerchantIdentifier: "YOUR MERCHANT ID", countryCode: "YOUR MERCHANT COUNTRY CODE")
paymentRequest.supportedNetworks = ... // set up supported payment networks
checkoutSettings.applePayPaymentRequest = paymentRequest
let checkoutProvider = OPPCheckoutProvider(paymentProvider: provider, checkoutID: checkoutID!, settings: checkoutSettings)

Set button type (optional)

Choose a button type that matches the purchase flow. For design guidance, see Apple Pay button guidelines.

// Set button type to Buy
checkoutSettings.applePayType = PKPaymentButtonTypeBuy;
// Set button type to Buy
checkoutSettings.applePayType = PKPaymentButtonType.buy

See PKPaymentButtonType documentation for all options. The default value is plain.

Present checkout and handle callbacks

[checkoutProvider presentCheckoutWithPaymentBrand:@"APPLEPAY"
  loadingHandler:^(BOOL inProgress) {
    // Start or stop loading animation based on inProgress parameter.
} completionHandler:^(OPPTransaction * _Nullable transaction, NSError * _Nullable error) {
    if (error) {
        // Check code attribute (OPPErrorCode) and NSLocalizedDescription to find the reason.
    } else {
        if (transaction.redirectURL) {
            // Shopper was redirected to the issuer web page.
            // Request payment status when shopper returns using transaction.resourcePath or checkout ID.
        } else {
            // Request payment status for the synchronous transaction using transactionPath.resourcePath or checkout ID.
        }
    }
} cancelHandler:^{
    // Executed if the shopper closes the payment page early.
}];
checkoutProvider?.presentCheckout(withPaymentBrand: "APPLEPAY", 
   loadingHandler: { (inProgress) in
    // Executed whenever SDK sends request to the server or receives the answer. 
    // You can start or stop loading animation based on inProgress parameter.
}, completionHandler: { (transaction, error) in
    if error != nil {
        // See code attribute (OPPErrorCode) and NSLocalizedDescription to identify the reason of failure.
    } else {
        if transaction?.redirectURL != nil {
            // Shopper was redirected to the issuer web page.
            // Request payment status when shopper returns to the app using transaction.resourcePath or just checkout id.
        } else {
            // Request payment status for the synchronous transaction from your server using transactionPath.resourcePath or just checkout id.
        }
    }
}, cancelHandler: { 
    // Executed if the shopper closes the payment page prematurely.
})

Use the brand APPLEPAYTKN when the acquirer should handle decryption. Contact the acquirer for details.

Collecting Apple Pay shipping and billing information

Configure the payment request

Use requiredShippingContactFields and requiredBillingContactFields to specify needed information. The Apple Pay form prompts shoppers to enter the requested details.

// Enable requesting shipping information
if (@available(iOS 11.0, *)) {
    paymentRequest.requiredShippingContactFields = [NSSet setWithObject:PKContactFieldPostalAddress];
} else {
    paymentRequest.requiredShippingAddressFields = PKAddressFieldPostalAddress;
}

// Enable requesting billing information
if (@available(iOS 11.0, *)) {
    paymentRequest.requiredBillingContactFields = [NSSet setWithObject:PKContactFieldPostalAddress];
} else {
    paymentRequest.requiredBillingAddressFields = PKAddressFieldPostalAddress;
}
if #available(iOS 11.0, *) {
    paymentRequest.requiredShippingContactFields = Set([PKContactField.postalAddress])
} else {
    paymentRequest.requiredShippingAddressFields = .postalAddress
}
if #available(iOS 11.0, *) {
    paymentRequest.requiredBillingContactFields = Set([PKContactField.postalAddress])
} else {
    paymentRequest.requiredBillingAddressFields = .postalAddress
}

Adopt the OPPCheckoutProviderDelegate protocol

To receive callbacks from the Apple Pay form, make sure your view controller adopts the OPPCheckoutProviderDelegate protocol and set the checkoutProvider.delegate property.

// Adopt the OPPCheckoutProviderDelegate protocol
@interface SomeViewController () <OPPCheckoutProviderDelegate>
@end

@implementation SomeViewController
...
- (IBAction)applePayTapped:(id)sender {
    // Set a delegate property for the OPPCheckoutProvider instance
    self.checkoutProvider.delegate = self;
    ...
}
@end
// Adopt the OPPCheckoutProviderDelegate protocol
class SomeViewController: UIViewController, OPPCheckoutProviderDelegate {
    ...
    @IBAction func applePayTapped(_ sender: UIButton) {
        // Set a delegate property for the OPPCheckoutProvider instance
        self.checkoutProvider.delegate = self
        ...
    }
}

Collecting shipping information

The Apple Pay form calls two delegates when the shopper provides shipping information:

  • Shipping contact: checkoutProvider:applePayDidSelectShippingContact:handler:
  • Shipping method: checkoutProvider:applePayDidSelectShippingMethod:handler:

Some shipping methods are not available in all areas or have different costs for different addresses. Update this information by returning a new list of shipping methods and summary items in the completion block.

Shipping contact

- (void)checkoutProvider:(OPPCheckoutProvider *)checkoutProvider 
    applePayDidSelectShippingContact:(PKContact *)contact 
                            handler:(void (^)(OPPApplePayRequestShippingContactUpdate * _Nonnull))completion {

    // You may want to provide different shipping methods based on shipping information
    NSArray *shippingMethods = [self shippingMethodsForContact:contact];
    
    // You may want to change amount of transaction (e.g. by adding tax) based on shipping information
    self.contact = contact;
    [self updateSummaryItems];
    
    OPPApplePayRequestShippingContactUpdate *update = [[OPPApplePayRequestShippingContactUpdate alloc] initWithErrors:nil paymentSummaryItems:self.summaryItems shippingMethods:shippingMethods];
    completion(update);
}
func checkoutProvider(_ checkoutProvider: OPPCheckoutProvider, applePayDidSelectShippingContact contact: PKContact, handler completion: @escaping (OPPApplePayRequestShippingContactUpdate) -> Void) {
    // You may want to provide different shipping methods based on shipping information
    let shippingMethods = self.shippingMethods(contact: contact)

    // You may want to change amount of transaction (e.g. by adding tax) based on shipping information
    self.contact = contact
    updateSummaryItems()

    let update = OPPApplePayRequestShippingContactUpdate.init(errors: nil, paymentSummaryItems: self.summaryItems, shippingMethods: shippingMethods)
    completion(update)
}
📘

To support privacy, Peach Payments keeps the shipping information provided in this delegate anonymous. The returned contact contains enough information to calculate shipping costs without revealing sensitive information about the shopper. Full shipping information is available after the shopper authorises the payment in the checkoutProvider:applePayDidAuthorizePayment:handler delegate.

Shipping method

- (void)checkoutProvider:(OPPCheckoutProvider *)checkoutProvider
    applePayDidSelectShippingMethod:(PKShippingMethod *)shippingMethod
                            handler:(void (^)(OPPApplePayRequestShippingMethodUpdate * _Nonnull))completion {
    
    // Change the amount of the transaction based on the shipping method cost
    self.shippingMethod = shippingMethod;
    [self updateSummaryItems];
    
    OPPApplePayRequestShippingMethodUpdate *update = [[OPPApplePayRequestShippingMethodUpdate alloc] initWithPaymentSummaryItems:self.summaryItems];
    completion(update);
}
func checkoutProvider(_ checkoutProvider: OPPCheckoutProvider, applePayDidSelect shippingMethod: PKShippingMethod, handler completion: @escaping (OPPApplePayRequestShippingMethodUpdate) -> Void) {
    // You may want to change amount of transaction based on shipping method cost
    self.shippingMethod = shippingMethod
    updateSummaryItems()

    let update = OPPApplePayRequestShippingMethodUpdate.init(paymentSummaryItems: self.summaryItems)
    completion(update)
}

Collecting billing information

Billing information and full shipping information is available after the shopper authorises the payment. Use the checkoutProvider:applePayDidAuthorizePayment:handler delegate to get this data.

- (void)checkoutProvider:(OPPCheckoutProvider *)checkoutProvider
    applePayDidAuthorizePayment:(PKPayment *)payment
                        handler:(void (^)(OPPApplePayRequestAuthorizationResult * _Nonnull))completion {
                        
    OPPApplePayRequestAuthorizationResult *result = nil;
    
    // You may want to validate shipping/billing info
    if ([self isValidBillingContact:payment.billingContact]) {
        // Return success to continue the payment
        result = [[OPPApplePayRequestAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusSuccess 
                                                                        errors:nil];
    } else {
        NSArray *errors = nil;
        if (@available(iOS 11.0, *)) {
            // Since iOS 11 you can pass list of errors in billing/shipping info
            NSError *error = [PKPaymentRequest paymentBillingAddressInvalidErrorWithKey:CNPostalAddressCountryKey
                                                                   localizedDescription:@"Some country error message"];
            errors = [NSArray arrayWithObject:error];
        }
        result = [[OPPApplePayRequestAuthorizationResult alloc] initWithStatus:PKPaymentAuthorizationStatusFailure
                                                                        errors:errors];
    }
    completion(result);
}
func checkoutProvider(_ checkoutProvider: OPPCheckoutProvider, applePayDidAuthorizePayment payment: PKPayment, handler completion: @escaping (OPPApplePayRequestAuthorizationResult) -> Void) {
    var result: OPPApplePayRequestAuthorizationResult

    // You may want to validate shipping/billing info
    if isValidBillingContact(contact: payment.billingContact) {
        // Return success to continue the payment
        result = OPPApplePayRequestAuthorizationResult.init(status: .success, errors: nil)
    } else {
        var errors: [Error] = []
        if #available(iOS 11.0, *) {
            // Since iOS 11 you can pass list of errors in billing/shipping info
            let error = PKPaymentRequest.paymentBillingAddressInvalidError(withKey: CNPostalAddressCountryKey, localizedDescription: "Some country error mesage")
            errors = [error]
        }
        result = OPPApplePayRequestAuthorizationResult.init(status: .failure, errors: errors)
    }

    completion(result)
}

Updating the Apple Pay transaction amount

To update the Apple Pay transaction amount:

  1. Send an updated list of PKPaymentSummaryItems in the shipping information delegates.
  2. Update the checkout session with the new amount by a server-to-server call. Do this asynchronously in the checkoutProvider:continueSubmitting:completion: delegate, which is the last checkout delegate called before submitting the transaction.
- (void)checkoutProvider:(OPPCheckoutProvider *)checkoutProvider
    continueSubmitting:(OPPTransaction *)transaction
            completion:(void (^)(NSString * _Nullable checkoutID, BOOL abort))completion {
    
    // Update the checkout session with the new amount
    completion(transaction.paymentParams.checkoutID, NO);
}
func checkoutProvider(_ checkoutProvider: OPPCheckoutProvider, continueSubmitting transaction: OPPTransaction, completion: @escaping (String?, Bool) -> Void) {
    // Update checkout session with the new amount
    completion(transaction.paymentParams.checkoutID, false)
}

Submitting shopper information

Instead of collecting information manually as explained above, use the mSDK to collect and submit it. Configure CheckoutSettings to specify the information to submit.

Configure CheckoutSettings

Set the applePayContactTypes property in CheckoutSettings to specify the information to submit.

  • OPPCheckoutApplePayContactTypeBillingAddress: The shopper billing address.
  • OPPCheckoutApplePayContactTypeCustomer: The shopper's name, surname, phone, and email.

Use one or both options.

checkoutSettings.applePayContactTypes = OPPCheckoutApplePayContactTypeBillingAddress | OPPCheckoutApplePayContactTypeCustomer;
checkoutSettings.applePayContactTypes = [.billingAddress, .customer]

Mobile SDK and your own UI

Accepting Apple Pay with a custom UI is like handling card payments. When the shopper approves a payment, the app receives a PKPayment object with encrypted card details. Create OPPApplePayPaymentParams with the payment data and submit a transaction.

Set up an Apple Pay button

Apple Pay is available to a subset of iOS users. Before presenting the Apple Pay option to the current user, you should determine whether Apple Pay is available. You can do it using OPPPaymentProvider helper method.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.applePayButton.enabled = [OPPPaymentProvider deviceSupportsApplePay];
}
override func viewDidLoad() {
    super.viewDidLoad()
    applePayButton.isEnabled = OPPPaymentProvider.deviceSupportsApplePay()
}

Create a PKPaymentRequest

Next, when the shopper taps "pay by Apple Pay", create a PKPaymentRequest object.

Peach Payments provides a helper method in OPPPaymentProvider to create one with the given merchantIdentifier, countryCode, and the following default values:

If you do not use the helper method, make sure that the merchantCapabilities always contain threeDSecure. The emv value is not supported.

📘

See how to register a merchant ID in Apple Pay documentation.

PKPaymentRequest *request = [OPPPaymentProvider paymentRequestWithMerchantIdentifier:@"YOUR MERCHANT ID" countryCode:@"YOUR COUNTRY CODE"];

// Set currency.
request.currencyCode = "USD";

// Create total item. Label should represent your company.  
// It will be prepended with "Pay" (i.e. "Pay Sportswear $100.00")
NSDecimalNumber *amount = [NSDecimalNumber decimalNumberWithMantissa:10000 exponent:-2 isNegative:NO];
request.paymentSummaryItems = @[[PKPaymentSummaryItem summaryItemWithLabel:@"Sportswear" amount:amount]];
let request = OPPPaymentProvider.paymentRequest(withMerchantIdentifier: "YOUR MERCHANT ID", countryCode: "YOUR COUNTRY CODE")

// Set currency. 
request.currencyCode = "USD"

// Create total item. Label should represent your company.  
// It will be prepended with the word "Pay" (i.e. "Pay Sportswear $100.00")
let amount = NSDecimalNumber(mantissa: 10000, exponent: -2, isNegative: false)
request.paymentSummaryItems = [PKPaymentSummaryItem(label: "Sportswear", amount: amount)]

Present a PKPaymentAuthorizationViewController

Next, create and present a PKPaymentAuthorizationViewController with your payment request. Use the OPPPaymentProvider method to validate the final PKPaymentRequest object.

📘

Your view controller should adopt the PKPaymentAuthorizationViewControllerDelegate protocol to respond to user interaction with that view controller.

- (void)applePayTapped {
    PKPaymentRequest *request = ...; // See above
    if ([OPPPaymentProvider canSubmitPaymentRequest:request]) {
        PKPaymentAuthorizationViewController *vc = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
        vc.delegate = self;
        [self presentViewController:vc animated:YES completion:nil];
    } else {
        NSLog(@"Apple Pay not supported.");
    }
}
- (void)applePayTapped { 
    PKPaymentRequest *request = ...; // See above 
    if ([OPPPaymentProvider canSubmitPaymentRequest:request]) { 
        PKPaymentAuthorizationViewController *vc = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request]; 
        vc.delegate = self; 
        [self presentViewController:vc animated:YES completion:nil]; 
    } else { 
        NSLog(@"Apple Pay not supported."); 
    } 
}

Lastly, adopt the required PKPaymentAuthorizationViewControllerDelegate methods. In your implementation of paymentAuthorizationViewController:didAuthorizePayment:completion:, create OPPApplePayPaymentParams and submit a debit transaction.

- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller  
                       didAuthorizePayment:(PKPayment *)payment  
                                completion:(void (^)(PKPaymentAuthorizationStatus))completion {
    OPPApplePayPaymentParams *params = [OPPApplePayPaymentParams applePayPaymentParamsWithCheckoutID:checkoutID  
                                                                                           tokenData:payment.token.paymentData];
    // Check if parameters are valid and submit transaction.
    [self.provider submitTransaction:[OPPTransaction transactionWithPaymentParams:params] completionHandler:^(OPPTransaction *transaction, NSError *error) {
        if (error) {
            // See code attribute (OPPErrorCode) and NSLocalizedDescription to identify the reason of failure.
        } else {
            // Send request to your server to obtain transaction status.
        }
    }];
}

- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller {
    [controller dismissViewControllerAnimated:YES completion:nil];
}
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller  
                       didAuthorizePayment:(PKPayment *)payment  
                                completion:(void (^)(PKPaymentAuthorizationStatus))completion { 
    OPPApplePayPaymentParams *params = [OPPApplePayPaymentParams applePayPaymentParamsWithCheckoutID:checkoutID  
                                                                                           tokenData:payment.token.paymentData]; 
    // Check if parameters are valid and submit transaction.
    [self.provider submitTransaction:[OPPTransaction transactionWithPaymentParams:params] completionHandler:^(OPPTransaction *transaction, NSError *error) {
        if (error) {
            // See code attribute (OPPErrorCode) and NSLocalizedDescription to identify the reason of failure.
        } else {
            // Send request to your server to obtain transaction status.
    }];
} 
 
- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller { 
    [controller dismissViewControllerAnimated:YES completion:nil]; 
} 

You can also integrate APPLEPAYTKN in same way as an Apple Pay, you just need to pass the brand APPLEPAYTKN while creating OPPApplePayPaymentParams and submit a transaction.

NSError *error;
    OPPApplePayPaymentParams *params = [[OPPApplePayPaymentParams alloc] initWithCheckoutID:checkoutID paymentBrand:@"APPLEPAYTKN" tokenData:payment.token.paymentData error:&error];
let params = try? OPPApplePayPaymentParams(checkoutID: checkoutID, paymentBrand: "APPLEPAYTKN", tokenData: payment.token.paymentData)

Google Pay

Requirements

Configuration

Open the build.gradle file in the app module and add the following to the dependencies block:

implementation "com.google.android.gms:play-services-wallet:x.x.x"

Add Google Pay to your app in one of two ways, depending on whether you use the ready-to-use UI or the SDK with your own UI. The sections below cover these two approaches. Follow the instructions for your chosen approach.

Ready-to-use UI

Add GOOGLEPAY payment brand

Create the CheckoutSettings, and add GOOGLEPAY to the payment brands list:

Set<String> paymentBrands = new HashSet<String>();
paymentBrands.add("GOOGLEPAY");

CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, Connect.ProviderMode.TEST);
val paymentBrands = hashSetOf("GOOGLEPAY") 
 
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, Connect.ProviderMode.TEST)

If you integrate Google Pay using the drop-in buttons, set the GOOGLEPAY payment brand in the PaymentButtonFragment.

paymentButtonFragment.setPaymentBrand("GOOGLEPAY");
paymentButtonFragment.paymentBrand = "GOOGLEPAY"

Configure PaymentDataRequest

Create JSONObject to configure Google Pay widget with the transaction information, allowed payment methods, and card networks. Use PaymentDataRequestJsonBuilder to create the base paymentDataRequestJson object with all required parameters.

Then, add optional configurations, for example, enable requesting shipping information in the widget. Refer to the Google Pay Payments API documentation for more options.

JSONArray allowedPaymentMethodsJson = new JSONArray()
        .put(new CardPaymentMethodJsonBuilder()
                    .setAllowedAuthMethods(new JSONArray()
                            .put("PAN_ONLY")
                            .put("CRYPTOGRAM_3DS")
                    )
                    .setAllowedCardNetworks(new JSONArray()
                            .put("VISA")
                            .put("MASTERCARD")
                            .put("AMEX")
                            .put("DISCOVER")
                            .put("JCB")
                    )
                    .setGatewayMerchantId("yourEntityId")
                    .toJson()
        );

JSONObject transactionInfoJson = new TransactionInfoJsonBuilder()
        .setCurrencyCode("USD")
        .setTotalPriceStatus("FINAL")
        .setTotalPrice("100.00")
        .toJson();

JSONObject paymentDataRequestJson = new PaymentDataRequestJsonBuilder()
        .setAllowedPaymentMethods(allowedPaymentMethodsJson)
        .setTransactionInfo(transactionInfoJson)
        .toJson();
val allowedPaymentMethodsJson = JSONArray()
        .put(CardPaymentMethodJsonBuilder()
                    .setAllowedAuthMethods(JSONArray()
                            .put("PAN_ONLY")
                            .put("CRYPTOGRAM_3DS")
                    )
                    .setAllowedCardNetworks(JSONArray()
                            .put("VISA")
                            .put("MASTERCARD")
                            .put("AMEX")
                            .put("DISCOVER")
                            .put("JCB")
                    )
                    .setGatewayMerchantId("yourEntityId")
                    .toJson()
        )

val transactionInfoJson = TransactionInfoJsonBuilder()
        .setCurrencyCode("USD")
        .setTotalPriceStatus("FINAL")
        .setTotalPrice("100.00")
        .toJson()

val paymentDataRequestJson = PaymentDataRequestJsonBuilder()
        .setAllowedPaymentMethods(allowedPaymentMethodsJson)
        .setTransactionInfo(transactionInfoJson)
        .toJson()

Set it to the CheckoutSettings.

📘

For GOOGLEPAYTKN, Peach Payments has introduced a new method, setGateway(@NonNull String gatewayName), in the CardPaymentMethodJsonBuilder class. You must pass the gateway name provided by your acquirer when using this method.

new CardPaymentMethodJsonBuilder()
.setGateway("example_provider_gateway_name");
CardPaymentMethodJsonBuilder()
.setGateway("example_provider_gateway_name")
checkoutSettings.setGooglePayPaymentDataRequestJson(paymentDataRequestJson.toString());
checkoutSettings.googlePayPaymentDataRequestJson = paymentDataRequestJson.toString()

Collecting shopper information

You can collect extra information with the Google Pay widget before submitting the transaction, for example, shipping information.

Configure payment data request

In addition to the previous steps, complete the configuration of the paymentDataRequestJson object to request extra information from the shopper. Refer to the Google Pay API to see the full list of options.

JSONObject paymentDataRequestJson = new PaymentDataRequestJsonBuilder()
        .setAllowedPaymentMethods(allowedPaymentMethodsJson)
        .setTransactionInfo(transactionInfoJson)
        .setShippingAddressRequired(true)
        .setEmailRequired(true)
        .toJson();
val paymentDataRequestJson = PaymentDataRequestJsonBuilder()
        .setAllowedPaymentMethods(allowedPaymentMethodsJson)
        .setTransactionInfo(transactionInfoJson)
        .setShippingAddressRequired(true)
        .setEmailRequired(true)
        .toJson()

Configure receiving callbacks from the checkout

CheckoutActivity may send the callback CheckoutActivity.ACTION_ON_BEFORE_SUBMIT when the shopper submits the payment. To receive it, complete the following steps:

  1. Create a broadcast receiver to listen for intents from CheckoutActivity. The example below shows how to receive the PaymentData object with the requested data from the Google Pay widget. Refer to the Payments API for the class definition.

    public class CheckoutBroadcastReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
    
            if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT.equals(action)) {
                // Get shopper information for Google Pay
                PaymentData paymentData = intent.getParcelableExtra(CheckoutActivity.EXTRA_GOOGLE_PAY_PAYMENT_DATA);
    
                if (paymentData != null) {
                    // Validate PaymentData, if invalid, abort the transaction
                    // intent.putExtra(CheckoutActivity.EXTRA_TRANSACTION_ABORTED, true);
                    // If aborted, PaymentError with ERROR_CODE_TRANSACTION_ABORTED is returned in CheckoutActivityResult
                }
    
                String checkoutId = intent.getStringExtra(CheckoutActivity.EXTRA_CHECKOUT_ID);
                ComponentName senderComponentName = intent.getParcelableExtra(CheckoutActivity.EXTRA_SENDER_COMPONENT_NAME);
    
                // Return control to CheckoutActivity
                Intent checkoutIntent = new Intent(CheckoutActivity.ACTION_ON_BEFORE_SUBMIT);
                checkoutIntent.setComponent(senderComponentName);
                checkoutIntent.setPackage(senderComponentName.getPackageName());
                checkoutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                
                // EXTRA_CHECKOUT_ID is not required if the transaction is aborted
                checkoutIntent.putExtra(CheckoutActivity.EXTRA_CHECKOUT_ID, checkoutId);
                context.startActivity(checkoutIntent);
            }
        }
    }
     class BroadcastReceiver : BroadcastReceiver() { 
         
         override fun onReceive(context: Context, intent: Intent?) { 
             val action = intent?.action 
             
             if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT == action) { 
                 // get shopper information for the Google Pay
                 val paymentData: PaymentData? = intent.getParcelableExtra(CheckoutActivity.EXTRA_GOOGLE_PAY_PAYMENT_DATA) 
    
                 if (paymentData != null) {
                     // you can validate the PaymentData here, if data is not valid abort the transaction by adding
                     // intent.putExtra(CheckoutActivity.EXTRA_TRANSACTION_ABORTED, true)
                     // in this case the PaymentError with ERROR_CODE_TRANSACTION_ABORTED will be returned in the CheckoutActivityResult
                 }
     
                 val checkoutId = intent.getStringExtra(CheckoutActivity.EXTRA_CHECKOUT_ID) 
                 val senderComponent = intent.getParcelableExtra<ComponentName>(CheckoutActivity.EXTRA_SENDER_COMPONENT_NAME) 
     
                 // return control back to the CheckoutActivity
                 val checkoutIntent = Intent(CheckoutActivity.ACTION_ON_BEFORE_SUBMIT) 
                 checkoutIntent.component = senderComponent 
                 checkoutIntent.setPackage(senderComponent!!.packageName) 
                 checkoutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 
                 // the EXTRA_CHECKOUT_ID is not required if transaction is aborted
                 checkoutIntent.putExtra(CheckoutActivity.EXTRA_CHECKOUT_ID, checkoutId) 
     
                 context.startActivity(checkoutIntent) 
             } 
         } 
     }
  2. Declare the broadcast receiver in AndroidManifest.xml.

    <receiver
        android:name=".CheckoutBroadcastReceiver"
        android:exported="false" />
    📘

    Set android:exported="false" to ensure components from the same application or applications with the same user ID can send messages to the broadcast receiver.

  3. Use directed broadcasts for better security. Add ComponentName of the receiver to the CheckoutActivity intent.

    CheckoutSettings settings = new CheckoutSettings(...);
    ComponentName receiverComponentName = new ComponentName("yourPackageName", "yourReceiverClassName");
    Intent intent = checkoutSettings.createCheckoutActivityIntent(this, receiverComponentName);
    startActivityForResult(intent, CheckoutActivity.REQUEST_CODE_CHECKOUT);
     val checkoutSettings = CheckoutSettings(...)  
     val receiverComponent = ComponentName("yourPackageName", "yourReceiverClassName") 
     val intent: Intent = checkoutSettings.createCheckoutActivityIntent(this, receiverComponent) 
     
     startActivityForResult(intent, CheckoutActivity.REQUEST_CODE_CHECKOUT)

Submitting shopper information

Instead of collecting shopper information manually, use mSDK to collect and submit it.

Configure CheckoutSettings

Specify what information to submit:

  • GooglePaySubmitType.BILLING: Shopper billing address.
  • GooglePaySubmitType.CUSTOMER: Shopper name, surname, phone, and email.

Use one or both options.

checkoutSettings.setGooglePaySubmit(EnumSet.of(GooglePaySubmitType.BILLING, GooglePaySubmitType.CUSTOMER));
checkoutSettings.setGooglePaySubmit(EnumSet.of(GooglePaySubmitType.BILLING, GooglePaySubmitType.CUSTOMER))

Configure payment data request

The payment data request should include shopper information in the payment data response.

JSONArray allowedPaymentMethodsJson = new JSONArray()
        .put(new CardPaymentMethodJsonBuilder()
        .setAllowedAuthMethods(new JSONArray()
                .put("PAN_ONLY")
                .put("CRYPTOGRAM_3DS")
        )
        .setAllowedCardNetworks(new JSONArray()
                .put("VISA")
                .put("MASTERCARD")
        )
        .setGatewayMerchantId("entityId")
        .setBillingAddressRequired(true)
        .setBillingAddressParameters("FULL", true)
        .toJson()
);
                    
JSONObject paymentDataRequestJson = new PaymentDataRequestJsonBuilder()
        .setAllowedPaymentMethods(allowedPaymentMethodsJson)
        .setTransactionInfo(transactionInfoJson)
        // set shipping address required if you use GooglePaySubmitType.CUSTOMER,
        // some data can be taken from it if billing address is missing
        .setShippingAddressRequired(true)
        .setEmailRequired(true)
        .toJson();
val allowedPaymentMethods = JSONArray()
        .put(CardPaymentMethodJsonBuilder()
        .setAllowedAuthMethods(JSONArray()
                .put("PAN_ONLY")
                .put("CRYPTOGRAM_3DS")
        )
        .setAllowedCardNetworks(JSONArray()
                .put("VISA")
                .put("MASTERCARD")
        )
        .setGatewayMerchantId("entityId")
        .setBillingAddressRequired(true)
        .setBillingAddressParameters("FULL", true)
        .toJson()
)

val paymentDataRequestJson = PaymentDataRequestJsonBuilder()
        .setAllowedPaymentMethods(allowedPaymentMethodsJson)
        .setTransactionInfo(transactionInfoJson)
    // set shipping address required if you use GooglePaySubmitType.CUSTOMER,
    // some data can be taken from it if billing address is missing
        .setShippingAddressRequired(true)
        .setEmailRequired(true)
        .toJson()

Parameters mapping

OPP parameterGoogle Pay field
billing.street1paymentMethodData.info.billingAddress.address1
billing.street2paymentMethodData.info.billingAddress.address2 and paymentMethodData.info.billingAddress3
billing.citypaymentMethodData.info.billingAddress.locality
billing.statepaymentMethodData.info.billingAddress.administrativeArea
billing.postcodepaymentMethodData.info.billingAddress.postalCode
billing.countrypaymentMethodData.info.billingAddress.countryCode
OPP parameterGoogle Pay field
customer.givenNamepaymentMethodData.info.billingAddress.name (before first space)
customer.surnamepaymentMethodData.info.billingAddress.name (after first space)
customer.phonepaymentMethodData.info.billingAddress.phoneNumber or shippingAddress.phoneNumber
customer.emailemail

Confirmation page

Display a final price before processing the transaction. If the amount changes based on data from the Google Pay widget, show a confirmation page with the final price. Configure this in CheckoutActivity.ACTION_ON_BEFORE_SUBMIT.

  1. Initialise OrderSummary with order details, total amount, and Google Pay PaymentData response.

    PaymentData paymentData = intent.getParcelableExtra(CheckoutActivity.EXTRA_GOOGLE_PAY_PAYMENT_DATA);
    
    if (paymentData != null) {
        LinkedHashMap<String, Double> orderItems = new LinkedHashMap<String, Double>() {{
            put("Subtotal", 200.00);
            put("Shipping", 10.00);
        }};
    
        OrderSummary orderSummary = new OrderSummary(orderItems, 210.00, paymentData);
    }
     val paymentData: PaymentData? = intent.getParcelableExtra(CheckoutActivity.EXTRA_GOOGLE_PAY_PAYMENT_DATA) 
     
     if (paymentData != null) { 
         val orderItems = linkedMapOf( 
                 "Subtotal" to 200.00, 
                 "Shipping" to 10.00 
         ) 
         val orderSummary = OrderSummary(orderItems, 210.00, paymentData) 
     }
  2. Add OrderSummary extra to the CheckoutActivity intent.

    checkoutIntent.putExtra(CheckoutActivity.EXTRA_ORDER_SUMMARY, orderSummary);
    checkoutIntent.putExtra(CheckoutActivity.EXTRA_ORDER_SUMMARY, orderSummary)
  3. Start the checkout activity to return control to the SDK and show the confirmation page.

Mobile SDK and your own UI

Follow this tutorial to integrate Google Pay into your UI. Use PaymentDataRequestJsonBuilder to create the base paymentDataRequestJson object with all required parameters.

To make a transaction, create the GooglePayPaymentParams with a received token and actual card brand:

PaymentParams paymentParams = new GooglePayPaymentParams(checkoutId, token, cardBrand);
val paymentParams = GooglePayPaymentParams(checkoutId, token, cardBrand)
📘

Get the actual card brand from com.google.android.gms.wallet.PaymentData. If it is MASTERCARD, use MASTER as the card brand.

Submit the transaction:

Transaction transaction = null;

try {
    transaction = new Transaction(paymentParams);
    paymentProvider.submitTransaction(transaction);
} catch (PaymentException ee) {
    /* error occurred */
}
try { 
    val transaction = Transaction(paymentParams) 
    paymentProvider.submitTransaction(transaction) 
} catch (ee: PaymentException) { 
    /* error occurred */ 
}
📘

For GOOGLEPAYTKN, use the new paymentBrand parameter introduced in the constructor as the payment brand.

Samsung Pay

Samsung Pay is easy to set up and gives your customers a simple and secure way to pay using their Samsung devices. This guide explains how to process Samsung Pay payments with Peach Payments' SDK.

Requirements

To use Samsung Pay, you must create keys and a certificate signing request (CSR). Follow these steps:

  1. In the Smart Payments Platform, go to Administration > Mobile Payment, select Generate keys, and download the CSR.
  2. In the Samsung Pay Developer Centre, choose Create new service and upload the CSR.

Configuration

Add SamsungPaySDK JAR.

  1. Drag SamsungPaySDK_2.22.00.jar to the libs folder.

  2. Update the implementation in the build.gradle file. Add the .JAR with the .AAR:

    implementation fileTree(include: ["*.aar", "*.jar"], dir: "libs")
  3. Open the AndroidManifest.xml file and add the following metadata in the application tag:

    <meta-data android:name="debug_mode" android:value="Y" />
    <meta-data android:name="spay_debug_api_key" android:value="debug_api_key" />
    <meta-data android:name="spay_sdk_api_level" android:value="2.18" />
    📘

    Set debug_mode to N after Samsung's review and when ready to go live.

Adding Samsung Pay to your app depends on whether you use the ready-to-use UI or the SDK and your own UI. Follow the relevant section below.

Ready-to-use UI

📘

Add your applicationId to your app in the Samsung Pay Developer account. Otherwise, the Samsung Pay SDK does not return a payment token.

Add the Samsung Pay payment brand

Set<String> paymentBrands = new HashSet<>();
paymentBrands.add("SAMSUNGPAY");
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, Connect.ProviderMode.TEST);
val paymentBrands = hashSetOf("SAMSUNGPAY") 
 
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, Connect.ProviderMode.TEST)

Create SamsungPayConfig

Create SamsungPayConfig to configure Samsung Pay widget. It requires serviceId and CustomSheetPaymentInfo.

private SamsungPayConfig getSamsungPayConfig() {
    return new SamsungPayConfig(
        "serviceId",
        getCustomPaymentSheetInfo()
    );
}

private CustomSheetPaymentInfo getCustomPaymentSheetInfo() {
    return new CustomSheetPaymentInfo.Builder()
        .setMerchantName("Sample merchant")
        .setCustomSheet(getCustomSheet(getTransactionAmount(), application.getCurrency().name()))
        .build();
}

private CustomSheet getCustomSheet(double amount, String currencyCode) {
    AmountBoxControl amountBoxControl = new AmountBoxControl("amountControlId", currencyCode);
    amountBoxControl.setAmountTotal(amount, AmountConstants.FORMAT_TOTAL_PRICE_ONLY);

    CustomSheet customSheet = new CustomSheet();
    customSheet.addControl(amountBoxControl);

    return customSheet;
}
private fun getSamsungPayConfig(): SamsungPayConfig? {
    return SamsungPayConfig(
        "serviceId",
        getCustomPaymentSheetInfo())
}

private fun getCustomPaymentSheetInfo(): CustomSheetPaymentInfo? {
    return Builder()
        .setMerchantName("Sample merchant")
        .setCustomSheet(getCustomSheet(getTransactionAmount(),
            application.getCurrency().name()))
         .build()
}

private fun getCustomSheet(amount: Double, currencyCode: String): CustomSheet? {
    val amountBoxControl = AmountBoxControl("amountControlId", currencyCode)
    amountBoxControl.setAmountTotal(amount, AmountConstants.FORMAT_TOTAL_PRICE_ONLY)
    val customSheet = CustomSheet()
    customSheet.addControl(amountBoxControl)
    return customSheet
}

Set SamsungPayConfig in CheckoutSettings

checkoutSettings.setSamsungPayConfig(getSamsungPayConfig());
checkoutSettings.samsungPayConfig = getSamsungPayConfig()

Handle the card information update event

Samsung Pay provides the card information update event when the shopper changes the card in Samsung Pay. It passes the newly selected cardInfo, allowing the custom sheet to reflect the change. To handle this event, create SamsungPayConfig.CardInfoUpdateListener and set it to SamsungPayConfig.

public class SamsungPayCardInfoUpdateListener
        implements SamsungPayConfig.CardInfoUpdateListener {

    public SamsungPayCardInfoUpdateListener() {
        // do nothing
    }

    public SamsungPayCardInfoUpdateListener(@NonNull Parcel in) {
        // do nothing
    }

    @Override
    public void onCardInfoUpdated(@NonNull CardInfo selectedCardInfo,
                                  @NonNull CustomSheet customSheet) {
        // update custom sheet using selected card info
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel parcel,
                              int flags) {
        // do nothing
    }

    public static final Creator CREATOR
            = new Creator<SamsungPayCardInfoUpdateListener>() {
        @Override
        public SamsungPayCardInfoUpdateListener createFromParcel(Parcel in) {
            return new SamsungPayCardInfoUpdateListener(in);
        }

        @Override
        public SamsungPayCardInfoUpdateListener[] newArray(int size) {
            return new SamsungPayCardInfoUpdateListener[size];
        }
    };
}
class SamsungPayCardInfoUpdateListener() : SamsungPayConfig.CardInfoUpdateListener {

    constructor(parcel: Parcel) : this()

    override fun onCardInfoUpdated(selectedCardInfo: CardInfo,
                                   customSheet: CustomSheet) {
        // update custom sheet using selected card info
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        // do nothing
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator {
        override fun createFromParcel(parcel: Parcel): SamsungPayCardInfoUpdateListener {
            return SamsungPayCardInfoUpdateListener(parcel)
        }

        override fun newArray(size: Int): Array {
            return arrayOfNulls(size)
        }
    }
}
samsungPayConfig.setCardInfoUpdateListener(new SamsungPayCardInfoUpdateListener());
samsungPayConfig.cardInfoUpdateListener = SamsungPayCardInfoUpdateListener()
📘

If the total amount changes, use the ACTION_ON_BEFORE_SUBMIT callback of CheckoutActivity to send a checkout ID update request with the new amount. Perform this server-to-server.

Handle CustomSheetPaymentInfo before payment

The CheckoutActivity may send the callback when the shopper submits the payment. See Receiving callbacks during checkout process. Use this callback to check CustomSheetPaymentInfo and update the checkout if needed.

public class CheckoutBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(@NonNull Context context, @NonNull Intent intent) {
        if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT.equals(intent.getAction())) {
            CustomSheetPaymentInfo customSheetPaymentInfo = intent.getParcelableExtra(
                    CheckoutActivity.EXTRA_SAMSUNG_PAY_PAYMENT_DATA);
            // handle Samsung Pay custom sheet payment info
        }
    }
}
class BroadcastReceiver : BroadcastReceiver() { 
     
    override fun onReceive(context: Context, intent: Intent?) { 
        val action = intent?.action 
         
        if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT == action) { 
            val customSheetPaymentInfo: CustomSheetPaymentInfo = intent.getParcelableExtra(
                    CheckoutActivity.EXTRA_SAMSUNG_PAY_PAYMENT_DATA);

            // handle Samsung Pay custom sheet payment info
        } 
    } 
}

Mobile SDK and your own UI

Follow the Samsung Pay SDK documentation to receive the tokenised payment information for an in-app payment.

After you have the tokenised payment information, create SamsungPayPaymentParams and submit the transaction.

PaymentParams paymentParams = new SamsungPayPaymentParams(checkoutId, paymentCredential);
paymentParams.setShopperResultUrl("companyname://result");

try {
    OppPaymentProvider paymentProvider = new OppPaymentProvider(context, providerMode);
    paymentProvider.submitTransaction(new Transaction(paymentParams), transactionListener);
} catch (PaymentException e) {
    // handle payment exception
}
val paymentParams = SamsungPayPaymentParams(checkoutId, paymentCredential)
paymentParams.shopperResultUrl = "companyname://result"

try { 
     val paymentProvider = OppPaymentProvider(context, providerMode)
     paymentProvider.submitTransaction(Transaction(paymentParams), transactionListener) 
} catch (e: PaymentException) { 
     // handle payment exception
}