Performing a Checkout

The checkout process requires you to create a Checkout object with all the necessary components:

  • Line items
  • Billing address
  • Shipping address
  • Shipping charges
  • Discount, if applicable
  • Payment details

Once all of that information has been gathered, complete the checkout to create an order.

Building a Cart

BUYCart consists of one or more line items from product variants retrieved in the product list.

// Create a cart
BUYCart *cart = [client.modelManager insertCartWithJSONDictionary:nil];

// Add a product variant to the cart
// In this case, we're adding the first product and its first variant
BUYProduct *product = [self.products firstObject];
BUYProductVariant *variant = [product.variants firstObject];
[cart addVariant:variant];

Creating a Checkout

When the customer is ready to make a purchase, a BUYCheckout must be created from the BUYCart object.

BUYCheckout *checkout = [client.modelManager checkoutWithCart:cart];

// Sync the checkout with Shopify
[client createCheckout:checkout completion:^(BUYCheckout *checkout, NSError *error) {
    if (error == nil) {
        self.checkout = checkout;
        // optionally, save the checkout.token token somewhere on disk
    } else {
        // Handle errors here
    }
}];

Completing the Checkout

At this point you are ready to collect the payment information from the customer. If using Apple Pay, keep a reference to the PKPaymentToken. If using a credit card payment, prompt the customer to enter the credit card information (see Native Checkout).

Although the SDK supports payment by credit card, we highly recommend that you use Apple Pay as the only source of payment within your app. Familiarize yourself with the Mobile PCI Standards before including a full credit card checkout flow in your app.

The are three ways to complete a checkout:

Payment Providers

If your app does not require a custom checkout experience, you should use a payment provider to manage checkout for you. The SDK includes two provider types: Web and Apple Pay.

In order to use a payment provider, you do the following:

  1. Instantiate the provider type you intend to use (BUYApplePayPaymentProvider or BUYWebCheckoutPaymentProvider).
  2. Optionally, instantiate BUYPaymentController to keep shared instances of payment providers.
  3. Register the payment provider with the payment controller.
  4. Before starting checkout, set a delegate object on the payment provider.
  5. Send the -startCheckout: message to the payment provider, and include the checkout object.

Your payment provider delegate must adopt the BUYPaymentProviderDelegate protocol:

- (void)paymentProvider:(id <BUYPaymentProvider>)provider wantsControllerPresented:(UIViewController *)controller
{
    [self presentViewController:controller];
}

- (void)paymentProviderWantsControllerDismissed:(id <BUYPaymentProvider>)provider
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)paymentProvider:(id <BUYPaymentProvider>)provider didCompleteCheckout:(BUYCheckout *)checkout withStatus:(BUYStatus)status
{
    if (status == BUYStatusComplete) {
        // Now the checkout is complete and you can discard it, and clean up
        self.checkout = nil;
    }
    else {
        // status will be 'BUYStatusFailed'; handle error
    }
}

The payment provider will manage interactions with the view controller.. Your delegate only needs to present and dismiss the payment view controller.

To be notified when the checkout completes, you can implement -paymentProvider:didCompleteCheckout:withStatus:. Alternately, listen for the BUYPaymentProviderDidCompleteCheckoutNotificationKey notification.

Additional delegate methods are available to provide more fine-grained updates on the progress of the payment process.

Web Checkout

Web checkout is the simplest way to complete a checkout. Your only need to specify the line items on the checkout, and then start the checkout with the BUYWebCheckoutPaymentProvider. The web checkout will handle the rest.

BUYWebCheckoutPaymentProvider

To perform a web checkout, create and configure an instance of BUYWebCheckoutPaymentProvider. This class will launch Shopify web checkout in Safari. On iOS 9 and above, the provider will create an instance of SFSafariViewController to pass back to the delegate.

Did you know?

On iOS 8, web checkout uses Mobile Safari.

When the web checkout is dismissed, or when the user returns to your application from Safari, you will have to check the status of the checkout to confirm that checkout completed successfully, or if the user cancelled checkout.

Apple Pay

If you want to support Apple Pay, familiarize yourself with the Apple Pay Programming Guide.

Did you know?

Apple Pay does not support use of a test credit card. You are required to use a real credit card.

iOS 9 (and later) Simulator supports PKAuthorizationViewController, but we recommend performing a real transaction to ensure that Apple Pay is working correctly for your shop and app before submitting to iTunes Connect.

Ensure that Apple Pay is enabled in the Capabilities section of your application target, and a certificate has been uploaded to your Shopify admin.

BUYApplePayPaymentProvider

Use the BUYApplePayPaymentProvider class to facilitate payment with Apple Pay. This class will handle the work of showing the Apple Pay view controller.

Create an instance of BUYApplePayPaymentProvider, passing in an instance of BUYClient, and your Apple Pay merchant ID:

BUYApplePayPaymentProvider *applePayProvider = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:self.merchantID];
applePayProvider.delegate = self;
[applePayProvider startCheckout:self.checkout];

The payment provider will invoke the completion delegate callback -paymentProvider:didCompleteCheckout:withStatus:.

Native Checkout

If you want to provide a custom checkout and payment experience, you can do so using the native checkout interface.

Specifying Billing and Shipping Addresses

// Get the customer's address
BUYAddress *address = [client.modelManager insertAddressWithJSONDictionary:nil];
address.firstName = @"Egon";
address.lastName = @"Spengler";
address.address1 = @"421 8th Ave";
address.city = @"New York";
address.province = @"NY";
address.zip = @"10001";
address.countryCode = @"US";

// Add the shipping address, billing address, and email to the checkout
self.checkout.shippingAddress = address;
self.checkout.billingAddress = address;
self.checkout.email = egon.spengler@shopify.com;

// Update the checkout with the shipping address
[client updateCheckout:self.checkout completion:^(BUYCheckout *checkout, NSError *error) {
    if (error == nil) {
        self.checkout = checkout;
    } else {
        // Handle errors here
    }
}];

Once the checkout has been updated with the shipping address, the shipping rates can be retrieved.

[client getShippingRatesForCheckoutWithToken:checkout.token completion:^(NSArray<BUYShippingRate *> *shippingRates, BUYStatus status, NSError *error) {
    if (shippingRates && !error) {
        // Assumes this property exists on caller
        self.availableShippingRates = shippingRates;
    }
    else {
        // Handle error
    }
}];

Add the selected shipping method to the BUYCheckout object, and call updateCheckout to update on Shopify.

BUYShippingRate *selectedShippingRate;
self.checkout.shippingRate = selectedShippingRate;

// Update the checkout with the shipping rate
[client updateCheckout:self.checkout completion:^(BUYCheckout *checkout, NSError *error) {
    if (error == nil) {
        self.checkout = checkout;
    } else {
        // Handle errors here
    }
}];

Charging a credit card

BUYCreditCard *creditCard = [[BUYCreditCard alloc] init];
creditCard.number = @"4242424242424242";
creditCard.expiryMonth = @"12";
creditCard.expiryYear = @"20";
creditCard.cvv = @"123";
creditCard.nameOnCard = @"Dinosaur Banana";

// Associate the credit card with the checkout
[client storeCreditCard:creditCard checkout:self.checkout completion:^(NSString *paymentToken, NSError *error) {
    if (error == nil) {
        // Assume this property exists
        self.paymentToken = paymentToken;
    } else {
        // Handle errors here
    }
}];

Once the credit card has been stored on the checkout on Shopify, the checkout can be completed. This operation involves a number of steps, and implicitly must wait on remote payment processors, so it will take longer than most simple requests.

[client completeCheckoutWithToken:self.checkout.token paymentToken:self.paymentToken completion:^(BUYCheckout *returnedCheckout, NSError *error) {
    if (error == nil) {
        self.checkout = returnedCheckout;
    } else {
        // Handle errors here
    }
}];

Error Handling

It's essential to verify the error object returned with every completion block.

For example, if you were to complete a checkout without an email, the error.userInfo will be:

{
  "errors" = {
    "checkout" = {
      "email" = [               // One error associated with a "blank" (or missing) email.
        {
          "code" = "blank",
          "message" = "can't be blank"
        }],
       "line_items" = [],      // No errors associated with this
       "shipping_rate_id" = [] // No errors associated with this
  }
}