# Consolidated Checkout API
# 1/14/21 Config Meeting
```
Checkout {
onAuthenticate: () -> () = { }
onAuthStepUp: () -> () = { }
OnShippingChange? = nil (pending implementation?)
OnLoggedOut? = nil
CreateOrder? = nil,
OnApprove? = nil, // automatically make capture/authorize
OnCancel? = nil,
OnError? = nil,
function set(CheckoutConfig)
function start(
CreateOrder? = nil,
OnApprove? = nil, // automatically make capture/authorize
OnCancel? = nil,
OnError? = nil,
)
// Future state, post-MVP. Billing Agreements are likely separate experiences than one-off purchases.
function start(
CreateBillingAgreement,
OnApprove? = nil,
OnCancel? = nil,
OnError? = nil
)
function start(
CreateSubscription,
OnApprove? = nil,
OnCancel? = nil,
OnError? = nil
)
}
```
## Consolidated Checkout API (Future State)
### Checkout Config
```
CheckoutConfig {
String clientId
Environment environment
// iOS: rename from `payMode` to `userAction`
// Android: would replace `merchantURLQueryParams`
UserAction? userAction
returnUrl: String // com.paypal.checkoutsamples://paypalpay
// uriScheme
// universalLink
SettingsConfig(
EnableLogging
ShouldFailEligibility // Android
ChecksEligibility // iOS
)
}
enum UserAction {
Commit
Continue
}
```
### Checkout -> CheckoutSDK
```
Checkout {
function set(CheckoutConfig)
// Android - when mapping to DebugConfigManager, ensure that
// "://paypalpay" is stripped off before setting `merchantRedirectUrl`
// possible rename, maybe launch paysheet, maybe something else
function start(
CreateOrder, // for GA
OnApprove? = nil, // automatically make capture/authorize - for GA
OnCancel? = nil, // for GA
OnError? = nil, // for GA
OnShippingChange? = nil, // pending implementation
OnLoggedOut? = nil // pending implementation
)
}
```
### Checkout Flow
```
Application {
function onStart {
Checkout.set(CheckoutConfig)
}
}
CheckoutScreen {
function payWithPayPal {
Checkout.start(
createOrder { createOrderActions ->
// Server-side Call by Merchant
createOrderActions.setOrderId(orderId)
// or
// Client-side Call
actions.create(order) { orderId ->
// Merchant can log orderId
}
// Success callback is optional
actions.create(order)
}
)
}
}
CreateOrderActions {
function create(orderId: String)
function create(order: Order, successCallback: ((String) -> Unit)?)
}
```
### OnApprove
iOS
```swift
class ApprovalData {
let payerID: String
let ecToken: String
let intent: String
let returnURL: URL?
}
```
Android
```kotlin
data class Approval(
val data: ApprovalData,
val orderActions: OrderActions
)
data class ApprovalData(
val payerId: String,
val orderId: String,
val paymentId: String? = null
)
```
#### Questions
1. Should we return the above fields? Are there additional fields we should return?
2. Return enums instead of strings for certain fields? (intent, userAction)
### OnError
```kotlin=
class ErrorInfo(
// The actual error.
val error: Error (iOS) / Throwable (Android),
// The error reason.
val reason: String,
// Correlation IDs associated with debugging issues.
val correlationIds: CorrelationIDs,
// Pay token/EC-Token/OrderID
val payToken: String,
// SDK version
val nativeSdkVersion: String
)
class CorrelationIDs(
// Our correlation id associated when checking eligibility.
// GraphQL Query: mobileSDKEligibility
val eligibilityDebugID: String?,
// Our correlation id associated when checking eligibility for payment buttons.
// GraphQL Query: TBD
val fundingEligibilityDebugID: String?,
// Our correlation id associated when updating client config.
// GraphQL Query: updateClientConfig
val updateClientConfigDebugID: String?,
// Our correlation id associated when upgrading LSAT.
// GraphQL Query: upgradeLowScopeAccessToken
val lsatUpgradeDebugID: String?,
// Our correlation id associated when fetching user info.
// GraphQL Query: checkoutSession
val fetchPayloadDebugID: String?,
// Our correlation id associated when converting currency info.
// GraphQL Query: updateCurrencyConversionType
val currencyConversionDebugID: String?,
// Our correlation id associated when finishing the checkout flow.
// GraphQL Query: approvePayment
val finishCheckoutDebugID: String?
)
```
## Future Notes
1. We may want to consider offering debug hooks into the SDK's. Currently we allow failing eligibility on both platforms but it's handled a little differently. Might be nice to stremline this.
```
// Android within CheckoutConfig
shouldFailEligibilityCall: Boolean
// iOS within EnvironmentConfig.swift
EnvironmentConfig(
public var checksEligibility: Bool = true
)
```
2. Sync up on CheckoutConfig as source of truth over DebugConfigManager (mostly Android).
3. To investigate: can we create a billing agreement from the orders api
4. EC Token -> Renamed to Order ID
---
## Working Config
```
CheckoutConfig {
String clientId
// Android: migrate from "RunTimeEnvironment" to "Environment"
Environment environment
// iOS: remove `payToken` from CheckoutConfig initializer
// iOS: rename from `payMode` to `userAction`
// Android: would replace `merchantURLQueryParams`
PayMode? userAction
// Talk about url schemes & universal link
iOS -> URIScheme (Redirect URL) & Unverisal Link used for Analtyics
Android -> merchantUrlScheme & merchantRedirectUrl
// Return URL
-> Used for authentication & fallback with Android.
returnUrl: String // com.paypal.checkoutsamples://paypalpay
}
```
---
# Working Notes
## iOS Signatures (Current State)
## Callback Signatures:
```swift
public typealias AuthenticationCallback = (@escaping (AuthToken?, Error?) -> Void) -> Void
public typealias AuthStepUpCallback = (URI?, (@escaping (RedirectURI?, Error?) -> Void)) -> Void
public typealias ApprovalCallback = (Approval) -> Void
public typealias CancelCallback = () -> Void
public typealias ErrorCallback = (ErrorInfo) -> Void
public typealias LogoutCallback = () -> Void
public typealias ShippingChangeCallback = (ShippingChange, (@escaping (ShippingChangeState, ShippingChangeError?) -> Void)) -> Void
```
### Checkout Config
```swift
CheckoutConfig {
public var clientID: String
public var payToken: String?
public var payMode: PayMode?
public var environment: Environment
public var environmentConfig = EnvironmentConfig()
public var uriScheme: String
public var universalLink: String
public var onAuthenticate: AuthenticationCallback?
public var onAuthStepUp: AuthStepUpCallback?
public var onApprove: ApprovalCallback?
public var onCancel: CancelCallback?
public var onError: ErrorCallback?
public var onLogout: LogoutCallback?
public var onShippingChange: ShippingChangeCallback?
public var presentingViewController: UIViewController?
public init(
clientID: String,
payToken: String,
universalLink: String,
uriScheme: String,
onApprove: @escaping CheckoutConfig.ApprovalCallback,
onCancel: @escaping CheckoutConfig.CancelCallback,
onError: @escaping CheckoutConfig.ErrorCallback,
environment: Environment = .live
)
}
```
### Checkout Definition
```swift
// Checkout.swift
@objc(PPCheckout)
@objcMembers
public final class Checkout: NSObject, StaticIdentifiable {
@objc(setConfig:)
public static func set(config: CheckoutConfig) {
...
}
@objc(start:)
public static func start(experience: Experience = .native) {
...
}
}
```
### Checkout Flow
```swift
let config = CheckoutConfig(
...
)
Checkout.set(config: config) // Must be called before `start`
Checkout.start(experience: Experience = .native)
```
## Android Signatures (Current State)
```kotlin
// Currently implemented in Java
CheckoutConfig {
val runTimeEnvironment: RunTimeEnvironment (maps to CheckoutEnvironment)
val environment: CheckoutEnvironment
val clientId: String
val merchantUrlScheme: String
val merchantRedirectUrl: String
val merchantQueryParams: String[]
val payPalCheckoutCompleteListener: PayPalCheckoutCompleteListener
val shippingCallbacks: ShippingCallbacks
val isDebug: Boolean
val shouldFailEligibilityCall: Boolean
}
```
```kotlin
PayPalCheckoutSdk {
fun startCheckoutWithToken(
context: Context,
token: String,
checkoutConfig: CheckoutConfig
)
fun startCheckoutWithOrders(
context: Context,
order: Order,
orderCallbacks: OrderCallbacks,
checkoutConfig: CheckoutConfig
)
fun startCheckoutWithIntentUri(
context: Context,
originUri: Uri,
checkoutConfig: CheckoutConfig
)
}
```
```kotlin
CheckoutEnvironemt {
// Handles URLs for LIVE, SANDBOX, and STAGING
}
PayPalCheckoutCompleteListener {
fun onCheckoutComplete(params: HashMap<String, String>)
fun onCheckoutCancelled(cancelReason: CheckoutCancelReason, reason: String)
}
```
```kotlin
ShippingCallbacks {
fun onShippingChange(data: ShippingData, listener: ShippingCallbackListener)
}
```
```kotlin
ShippingCallbackListener {
fun onSuccess(refreshData: Boolean)
fun onSuccess(refreshData: Boolean, upgradedAccessToken: String, purchaseUnit: List<PurchaseUnit>)
fun onFailure(reason: String, shippingCallbackRequestType: ShippingCallbackRequestType)
}
```
### Checkout Flow Option 1
```kotlin
val checkoutConfig = CheckoutConfig.getInstance().apply { /* set configuration values */ }
PayPalCheckoutSdk.getInstance().startCheckoutWithToken(this@MainActivity, "EC-TOKEN", checkoutConfig)
```
### Checkout Flow Option 2
```kotlin
val checkoutConfig = CheckoutConfig.getInstance().apply { /* set configuration values */ }
PayPalCheckoutSdk.getInstance().initialize(checkoutConfig)
PayPalCheckoutSdk.getInstance().startCheckoutWithToken(this@MainActivity, "EC-TOKEN")
```