The PayPal Native Payments module in the PayPal SDK enables PayPal payments via a native UI experience in your app.
Follow these steps to add PayPal Native Payments:
- Setup a PayPal Developer Account
- Add PayPal Native Payments Module
- Test and go live
- After initial setup, Follow instructions here for Billing Agreements
You will need to set up authorization to use the PayPal Payments SDK. Follow the steps in Get Started to create a client ID.
You will need a server integration to create an order to capture funds using the PayPal Orders v2 API.
In Xcode, add the PayPal SDK as a package dependency to your Xcode project. Enter https://github.com/paypal/iOS-SDK as the package URL. Tick the PayPalNativePayments
checkbox to add the PayPal Native Payments library to your app.
Include the PayPalNativePayments
sub-module in your Podfile
:
pod 'PayPal/PayPalNativePayments'
Create a CoreConfig
using an client id:
let config = CoreConfig(clientID: "<CLIENT_ID>", environment: .sandbox)
Create a PayPalNativeCheckoutClient
to approve an order with a PayPal payment method:
let paypalNativeClient = PayPalNativeCheckoutClient(config: config)
When a user initiates a payment flow, call v2/checkout/orders
to create an order and obtain an order ID:
Request
curl --location --request POST 'https://api.sandbox.paypal.com/v2/checkout/orders/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <ACCESS_TOKEN>' \
--data-raw '{
"intent": "<CAPTURE|AUTHORIZE>",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "5.00"
}
}
]
}'
Response
{
"id":"<ORDER_ID>",
"status":"CREATED"
}
The id
field of the response contains the order ID to pass to your client.
To start the PayPal Native checkout flow, call the start
function on PayPalNativeCheckoutClient
, with a PayPalNativeCheckoutRequest
using your order ID from step 3:
let request = PayPalNativeCheckoutRequest(orderID: "<ORDER_ID>")
await paypalNativeClient.start(request: request)
Implement PayPalNativeCheckoutDelegate
to listen for result notifications from the SDK. In this example, we implement it in a view model:
extension MyViewModel: PayPalNativeCheckoutDelegate {
func setup() {
paypalNativeClient.delegate = self
}
func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithResult result: PayPalNativeCheckoutResult) {
// order was successfully approved and is ready to be captured/authorized (see step 5)
}
func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithError error: CoreSDKError) {
// handle the error by accessing `error.localizedDescription`
}
func paypalDidCancel(_ payPalClient: PayPalNativeCheckoutClient) {
// the user cancelled
}
func paypalWillStart(_ payPalClient: PayPalNativeCheckoutClient) {
// the paypal pay sheet is about to appear. Handle loading views, spinners, etc.
}
}
For a working example please refer to PayPalViewModel in our Demo application
PayPalNativeShippingDelegate
if your order ID was created with shipping_preference
= GET_FROM_FILE
. If you created your order ID with shipping_preference
= NO_SHIPPING
or SET_PROVIDED_ADDRESS
, skip this step.
PayPalNativeShippingDelegate
notifies your app when the user updates their shipping address or shipping method details. You are required to PATCH
the order details on your server if the shipping method (or amount) changes. Do this with the PayPal Orders API - Update order functionality.
extension MyViewModel: PayPalNativeShippingDelegate {
func setup() {
paypalNativeClient.delegate = self // always required
paypalNativeClient.shippingDelegate = self // required for `GET_FROM_FILE` orders
}
func paypal(
_ payPalClient: PayPalNativeCheckoutClient,
didShippingAddressChange shippingAddress: PayPalNativeShippingAddress,
withAction shippingActions: PayPalNativePaysheetActions
) {
// called when the user updates their chosen shipping address
// REQUIRED: you must call shippingActions.approve() or shippingActions.reject() in this callback
shippingActions.approve()
// OPTIONAL: you can optionally patch your order. Once complete, call shippingActions.approve() if successful or shippingActions.reject() if not.
}
func paypal(
_ payPalClient: PayPalNativeCheckoutClient,
didShippingMethodChange shippingMethod: PayPalNativeShippingMethod,
withAction shippingActions: PayPalNativePaysheetActions
) {
// called when the user updates their chosen shipping method
// REQUIRED: patch your order server-side with the updated shipping amount.
// Once complete, call `shippingActions.approve()` or `shippingActions.reject()`
if patchOrder() == .success {
shippingActions.approve()
} else {
shippingActions.reject()
}
}
}
If you want to show shipping options in the PayPal Native Paysheet, provide purchase_units[].shipping.options
when creating an orderID with the orders/v2
API on your server. Otherwise, our paysheet UI won't display any shipping options.
If you receive a successful result in the client-side flow, you can then capture or authorize the order.
Call authorize
to place funds on hold:
curl --location --request POST 'https://api.sandbox.paypal.com/v2/checkout/orders/<ORDER_ID>/authorize' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic <ENCODED_CLIENT_ID>' \
--data-raw ''
Call capture
to capture funds immediately:
curl --location --request POST 'https://api.sandbox.paypal.com/v2/checkout/orders/<ORDER_ID>/capture' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic <ENCODED_CLIENT_ID>' \
--data-raw ''
Note: Be sure that the endpoint you are calling aligns with the intent set on the order created in step 3.
Follow the Create sandbox account instructions to create a PayPal test account. When prompted to login with PayPal during the payment flow on your mobile app, you can log in with the test account credentials created above to complete the Sandbox payment flow.
Follow these instructions to prepare your integration to go live.