Pay with Apple Pay
As an iOS developer, you have probably heard of Apple Pay. Apple Pay lets iPhone users pay for purchases by using their iPhones. There are some great benefits in store for a developer to integrate Apple Pay in his or her app. For example, your customers don’t need to enter a credit card number, expiration date, and CVC numbers; or in some cases, shipping address and billing address before making a purchase. All of this means a faster and more convenient checkout process. Another benefit is that it’s supposed to be a much secure way to handling credit card charges. In this chapter, I will show how to implement the feature of Pay with Apple Pay and Stripe.
Overview of Apple Pay
As a matter of fact, Apple Pay only handles part of the whole charge process. Namely, Apple Pay has completed the following things:
Note To understand more about how Apple Pay works, please refer to https://developer.apple.com/apple-pay/Getting-Started-with-Apple-Pay.pdf.
Apple Pay and Stripe
Note In order to use Apple Pay, you’ll need to add the “Apple Pay” capability to your app in Xcode. This requires creating, first, a merchant ID with Apple. You can read more about that process from the tutorial https://stripe.com/docs/mobile/apple-pay.
In Chapter 12, I have shown how to add Stripe/ApplePay CocoaPod to your project. After you have the merchant ID and “Apple Pay” capability set up, you can take full advantage of what the Stripe Apple Pay SDK has to offer. These are the steps:
As you can see, the difference between Pay with Credit Card and Pay with Apple Pay is that you don’t need to create an EMABPayment instance and save it while dealing with Pay with Apple Pay—Apple Pay on a user’s device has the credit card information stored. Another benefit is that you don’t need to ask for the user’s shipping address; you can get it from the payment request.
Here is how to implement it, assuming you have registered a merchant ID through Apple Developer portal.
Write down your merchant ID with our EMABConstants class and name it kAppleMerchantID. Next, create a property to keep track of the PKPaymentRequest.
@property (nonatomic, strong) PKPaymentRequest *paymentRequest;
As mentioned before, you will need to use Apple’s PKPaymentAuthorizationViewController. This view controller has some delegate methods you need to implement, so declare it:
@interface EMABBagTableViewController()<PKPaymentAuthorizationViewControllerDelegate>
Also, in order to show the items the user is purchasing, you need to set up the item list confirming PKPaymentAuthorizationViewController API PKPaymentSummaryItem. In other words, you need to transfer our EMABOrderItem to PKPaymentSummaryItem. Here is the helper method for that:
- (NSArray *)summaryItemsForShippingMethod:(PKShippingMethod *)shippingMethod {
NSMutableArray *purchasedItems = [NSMutableArray arrayWithCapacity:[self.order.items count]];
for (EMABOrderItem *item in self.order.items) {
double total = item.quantity * item.product.unitPrice;
NSString *readable = [NSString stringWithFormat:@"%.2f",total];
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithString:readable];
PKPaymentSummaryItem *purchasedItem = [PKPaymentSummaryItem
summaryItemWithLabel:item.product.name amount:price];
[purchasedItems addObject:purchasedItem];
}
return [NSArray arrayWithArray:purchasedItems];
}
Basically, you create a mutable array, iterate EMABOrderItem, and instantiate PKPaymentSummaryItem based on an instance of EMABOrderItem. In the end, you return an immutable copy of the container array.
Take a close look at the following two lines of code:
NSString *readable = [NSString stringWithFormat:@"%.2f",total];
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithString:readable];
This is definitely not the best way to represent the currency amount because normally you only have a two-digit mantissa for the currency amount. The recommended way by Apple Pay API is this:
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:total exponent:-2 isNegative:NO];
Before you can initialize PKPaymentRequest, you need to know whether the user’s iPhone is configured to Pay with Apple Pay. Luckily, Stripe has a convenient method for you: -(BOOL) canSubmitPaymentRequest. If yes, proceed ahead. If no, you need to gracefully report it to the user.
You also need to have the user’s shipping address so you can mail the package. PKPaymentRequest has a method and setRequiredShippingAddressFields:PKAddressFieldPostalAddress for that.
-(IBAction)onApplePay:(id)sender{
NSString *merchantId = kAppleMerchantID;
self.paymentRequest = [Stripe paymentRequestWithMerchantIdentifier:merchantId];
if ([Stripe canSubmitPaymentRequest:self.paymentRequest]) {
[self.paymentRequest setRequiredShippingAddressFields:PKAddressFieldPostalAddress];
[self.paymentRequest setRequiredBillingAddressFields:PKAddressFieldPostalAddress];
self.paymentRequest.paymentSummaryItems = [self summaryItemsForShippingMethod:nil];
PKPaymentAuthorizationViewController *auth = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:self.paymentRequest];
auth.delegate = self;
if (auth) {
[self presentViewController:auth animated:YES completion:nil];
} else
[SVProgressHUD showErrorWithStatus:NSLocalizedString(@"Something Wrong", @"Something Wrong")];
} else {
[SVProgressHUD showErrorWithStatus:NSLocalizedString(@"Apple Pay is not enabled.
Please enable your Apple Pay or Pay with Credit Card.", @"")];
}
}
Next, present the PKPaymentAuthorizationViewController. The user taps to pay. What happens after the tap? Here’s how to implement the view controller’s delegate method:
-(void)paymentAuthorizationViewController:(nonnull PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(nonnull PKPayment *)payment completion:(nonnull void (^)(PKPaymentAuthorizationStatus))completion{
[self handlePaymentAuthorizationWithPayment:payment completion:nil];
}
Create the helper method handlePaymentAuthorizationWithPayment, then implement this method in “Creating a single-use token.” Note that you have also been given a block that takes a PKPaymentAuthorizationStatus. Call this function with either PKPaymentAuthorizationStatusSuccess or PKPaymentAuthorizationStatusFailure after all of your asynchronous code is finished executing. This is how the PKPaymentAuthorizationViewController knows when and how to update its UI.
- (void)handlePaymentAuthorizationWithPayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus))completion {
[[STPAPIClient sharedClient] createTokenWithPayment:payment
completion:^(STPToken *token, NSError *error) {
if (error) {
completion(PKPaymentAuthorizationStatusFailure);
return;
}
[self createBackendChargeWithToken:token completion:completion];
}];
}
Once you receive the charge token, call the Parse Cloud function to make a real charge:
- (void)createBackendChargeWithToken:(STPToken *)token
completion:(void (^)(PKPaymentAuthorizationStatus))completion {
[self chargeWithToken:token.tokenId];
}
Here is how to call the Cloud function:
-(void)chargeWithToken:(NSString *)tokenId{
[self.order saveInBackgroundWithBlock:^(BOOL success, NSError *error){
if (!error) {
__weak typeof(self) weakSelf = self;
NSDictionary *params = @{@"chargeToken":tokenId, @"orderId":weakSelf.order.
objectId};
[PFCloud callFunctionInBackground:@"ChargeToken" withParameters:params
block:^(NSString *message, NSError *error){
if (!error) {
[weakSelf queryForUnfinishedOrder];
}
}];
}
}];
}
By all means, you want to save the current order first. After the save action is finished, pass the Stripe token and the order ID to the back end. It’s a much safer practice. Once the charge is successfully run through, you make another API call to check whether the order status has been changed; also update your UI.
Summary
In this chapter, I briefly summarized how Apply Pay works and how you can implement the Pay with Apple Pay feature with Stripe’s Apple Pay library.
3.16.66.156