This documentation is deprecated and no longer maintained. For the most up-to-date information, Please visit   docs.portone.cloud.

Skip to main content

iOS Native SDK - Connect(V2)

1. Overview#

Integrate PortOne iOS Native SDK to enable secure and efficient payment processing in your iOS application. This guide will help you set up the SDK and start accepting payments seamlessly.


2. Video Tutorial#

For a comprehensive guide on integrating the PortOne iOS SDK and using the payment method flow, refer to the following video tutorials:


3. Sample App#

Explore the sample application for integration examples on GitHub.


4. Prerequisites#

Before starting the integration, ensure you have:

  1. PortOne Account: Create an account on PortOne to access their services.
  2. API Keys: Obtain your API keys (client key and secret key) from the PortOne portal under Settings -> API tab.
  3. Enable Payment Channels: Customize and enable the payment channels and methods according to your business requirements.
  4. iOS Application: Prepare an iOS application where you will integrate the PortOne SDK.
  5. Download the Framework: Get the latest framework from here.

5. Integration Steps#

5.1. Embed the Framework in Project#

  1. Add Framework:

    • Download the .xcframework from the version folder.
    • Drag and drop it into your Xcode project.
    • Go to General -> Frameworks, Libraries, and Embedded Content.
    • Set the framework to Embed & Sign.

    Framework Embed

5.2. Enable Deep Linking in iOS#

  1. Configure URL Schemes:

    • Open your project settings and navigate to Info.

    • Add URL schemes and identifiers under URL Types in info.plist:

      <key>CFBundleURLTypes</key>
      <array>
      <dict>
      <key>CFBundleTypeRole</key>
      <string>Editor</string>
      <key>CFBundleURLName</key>
      <string>checkout</string>
      <key>CFBundleURLSchemes</key>
      <array>
      <string>portone</string>
      </array>
      </dict>
      </array>
  2. Add URL Schemes for Other Apps:

    <key>LSApplicationQueriesSchemes</key>
    <array>
    <string>itms-appss</string>
    <string>zalopay</string>
    <string>line</string>
    <string>ascendmoney</string>
    </array>
  3. Support HTTP Connections:

    • Add the following to info.plist:

      <key>NSAppTransportSecurity</key>
      <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
      <key>NSAllowsLocalNetworking</key>
      <true/>
      <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      </dict>

5.3. Obtain JWT Token#

  1. Generate JWT Token:

    • Implement server-side logic to generate a JWT token using the portoneKey.
    • Retrieve the JWT token in your iOS app and store it securely.

    Authentication Guide

5.4. Generate Signature Hash#

  1. Create Signature Hash:

    • Use HmacSHA256 to generate a signature hash for the payload.

    Signature Guide

5.5. Initialize and Authorize SDK#

  1. Import SDK:

    import PortoneSDK
  2. Initialize Checkout:

    var checkout = Checkout(delegate: self, environment: "sandbox", redirectURL: "portone://checkout")
  3. Implement Delegate Methods:

    extension ViewController: CheckoutDelegate {
    func transactionResponse(response transactionResponse: TransactionResponse?) {
    if let response = transactionResponse {
    // Handle the response
    }
    }
    var viewController: UIViewController? {
    return self
    }
    func transactionErrorResponse(error: Error?) {
    print("Error", error)
    }
    }

Different Payment Methods#

Here's the aligned and organized version of your code and explanations for initializing new card payments, handling saved cards, and processing wallet transactions.


1. Initialize the New Card Payment#

To start a new card payment, you need to initialize the payment with the required details and configuration:

let config = prepareConfig()
let cardDetails = CardDetails(
token: nil,
cardNumber: "4242424242424242",
expiryMonth: "09",
expiryYear: "25",
cardHolderName: "SAM",
type: "mastercard",
cvv: "123",
key: "clientKey"
)
checkout?.initiateNewCardPayment(
config: config,
cardDetails: cardDetails,
jwtToken: "jwtToken",
clientKey: "portoneKey",
subMerchantKey: nil,
customerUUID: nil,
onCompletionHandler: { (result) in
switch result {
case .success(let response):
print(response) // Corrected typo from 'resposne' to 'response'
// Handle successful response here
case .failure(let error):
print(error)
// Handle error cases here
}
}
)

Prepare Configuration for Transaction#

Create and configure the transaction request details:

func prepareConfig() -> TransactionRequest {
let billingAddress = BillingAddress(
city: "VND",
countryCode: "VN",
locale: "en",
line1: "address1",
line2: "address2",
postalCode: "400202",
state: "Mah"
)
let billingDetails = BillingDetails(
billingName: "Test mark",
billingEmail: "markweins@gmail.com",
billingPhone: "+918341469169",
billingAddress: billingAddress
)
let shippingAddress = ShippingAddress(
city: "abc",
countryCode: "VN",
locale: "en",
line1: "address_1",
line2: "address_2",
postalCode: "400202",
state: "Mah"
)
let shippingDetails = ShippingDetails(
shippingName: "xyz",
shippingEmail: "xyz@gmail.com",
shippingPhone: "1234567890",
shippingAddress: shippingAddress
)
let orderDetails = OrderDetails(
id: "knb",
name: "kim nguyen bao",
price: 1000,
quantity: 1
)
let promoDiscount = 110.0
let charges = 100.0
self.totalAmount = totalAmount + charges - promoDiscount
let merchantDetails = MerchantDetails(
name: "Downy",
logo: "https://upload.wikimedia.org/wikipedia/commons/a/a6/Logo_NIKE.svg",
backUrl: "https://demo.chaiport.io/checkout.html",
promoCode: "Downy350",
promoDiscount: 35000,
shippingCharges: 0.0
)
return TransactionRequest(
portOneKey: "portoneKey",
key: "portoneKey",
merchantDetails: merchantDetails,
paymentChannel: "VNPAY",
paymentMethod: "VNPAY_ALL",
merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))",
amount: self.totalAmount,
currency: countryCode,
signatureHash: "123",
billingAddress: billingDetails,
shippingAddress: shippingDetails,
orderDetails: [orderDetails],
successURL: "https://test-checkout.chaiport.io/success.html",
failureURL: "https://test-checkout.chaiport.io/failure.html",
redirectURL: "portone1://checkout",
countryCode: countryCode,
routingEnabled: false,
routingParams: nil,
transactionType: UserDefaults.getTransactionType.code,
bankDetails: nil,
directBankTransferDetails: nil
)
}

Handle Success and Failure Cases#

Implement the CheckoutDelegate to handle transaction responses:

extension ViewController: CheckoutDelegate {
func transactionResponse(_ transactionResponse: TransactionResponse?) {
if let response = transactionResponse {
// Handle successful transaction response here
print("Transaction Response Data:", response)
}
}
// For web view to be open on top of the ViewController.
var viewController: UIViewController? {
return self
}
func transactionErrorResponse(_ error: Error?) {
print("Transaction Error:", error ?? "Unknown error")
}
}

Sample Success and Failure Callbacks#

Success:

TransactionResponse(
statusCode: Optional("2000"),
redirectUrl: nil,
channelOrderRef: Optional("chrg_test_5zoxqylqa9nzjfu1tn6"),
status: Optional("Success"),
merchantOrderRef: Optional("MERCHANT1715253092990"),
statusReason: Optional("SUCCESS"),
chaipayOrderRef: nil,
isSuccess: nil,
orderRef: Optional("2gEBwTPb4W41RVYbjerMQaK2oGR"),
deepLink: nil,
amount: Optional("15001"),
linkOrderRef: Optional(""),
tokenizationPossible: nil,
message: Optional("The transaction was successful")
)

Failure:

NetworkError(
httpStatusCode: 400,
code: nil,
message: Optional("should be a valid card number"),
statusReason: nil,
isSuccess: nil
)

Sample JWT Token#

Ensure the JWT token is properly formatted and used for authentication:

let jwtToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJDSEFJUEFZIiwic3ViIjoibHpyWUZQZnlNTFJPYWxsWiIsImlhdCI6MTYzMjM5MDkyMCwiZXhwIjoyNzMyMzkwOTIwfQ.IRgiM-zjAdJEVDuPSNfxmDszZQi_csE1q7xjVRvPvoc"

2. Fetch Saved Cards#

To fetch saved credit card details, follow these steps:

a. Generate OTP#

Call the getOTP method to send an OTP to the user mobile number:

checkOut?.getOTP(self.numberTextField.text ?? "") { result in
switch result {
case .success(let data):
print("OTP Data:", data)
case .failure(let error):
print("Error:", error)
}
}

b. Fetch Saved Cards#

Once the OTP is received, use it along with the mobile number to fetch saved cards:

checkOut?.fetchSavedCards(
portOneKey: UserDefaults.getPortoneKey!,
mobileNumber: self.numberTextField.text ?? "",
otp: receivedOTP, // OTP received from previous step
token: nil, // Optionally, pass a token to skip OTP verification
onCompletionHandler: { result in
switch result {
case .success(let data):
// Handle the fetched saved cards
print("Saved Cards Data:", data)
case .failure(let error):
// Handle error
print("Error:", error)
}
}
)

Sample Responses#

Success Response:

{
"content": [
{
"token": "97daee740bb84a6d907dfe46fca1139e",
"partial_card_number": "4242 4242 4242 4242",
"expiry_month": "09",
"expiry_year": "2034",
"type": "visa",
"payment_channel_token": {
"OMISE_CUSTOMER_TOKEN": "cust_test_5va5euv4r4cf68gwcmm",
"STRIPE_CHANNEL_TOKEN": "pm_1Mqyk7CzOuxzGzz2njn9bFFE",
"STRIPE_CUSTOMER_TOKEN": "cus_NcDA43aAVrjiMp"
}
},
// More saved cards
],
"status_code": "2000",
"status_reason": "SUCCESS"
}

Failure Response:

{
"message": "Invalid JWT Token / Client Key. Error in parsing JWT Token.",
"status_code": "4010",
"status_reason": "INVALID_UNAUTHORISED_TRANSACTION_ERROR"
}

c. Process Payment with Saved Card#

Initialize the payment with the details of a saved card:

let cardDetails = CardDetails(
token: savedCard.token,
cardNumber: savedCard.partialCardNumber,
expiryMonth: savedCard.expiryMonth,
expiryYear: savedCard.expiryYear,
cardHolderName: " ",
type: savedCard.type,
cvv: "100", //
CVV for saved cards (may vary based on the API requirements)
savedCard: true,
key: "portoneKey"!
)
checkout?.initiateSavedCardPayment(
config: prepareConfig(type: .somePaymentMethod),
cardDetails: cardDetails,
onCompletionHandler: { result in
switch result {
case .success(let response):
// Handle successful payment response
print("Payment Response Data:", response)
case .failure(let error):
// Handle error
print("Error:", error)
}
}
)

3. Wallet Transactions#

To initiate wallet payments, use the initiatePayment method:

// Prepare the configuration for wallet payment
let payload = prepareConfig(type: .walletPaymentMethod)
checkout?.initiatePayment(payload, subMerchantKey: nil) { result in
switch result {
case .success(let data):
// Handle success
print("Payment Data:", data)
case .failure(let error):
// Handle error
print("Error:", error)
}
}

a. Handle Responses from Delegate#

Implement the CheckoutDelegate to handle transaction responses:

extension ViewController: CheckoutDelegate {
func transactionResponse(_ transactionResponse: TransactionResponse?) {
if let response = transactionResponse {
// Populate data or handle the successful response
print("Transaction Response Data:", response)
}
}
func transactionErrorResponse(_ error: Error?) {
print("Transaction Error:", error ?? "Unknown error")
}
}

Sample Response

// Success
{
"is_success": true,
"redirect_url": "https://api.omise.co/payments/paym_test_5vs49nt75qk2i3jbkvi/authorize",
"channel_order_ref": "chrg_test_5vs49nqfr5bqq2er4ae",
"merchant_order_ref": "MERCHANT1683899406422",
"order_ref": "2PpGPD8ccxL5thPjMwwPN6KaNMc",
"message": "3DS is forced for this transaction",
"deep_link": "",
"additional_data": null
}
// failure
{
"chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf",
"channel_order_ref": "0",
"merchant_order_ref": "MERCHANT1628681469666",
"status": "Initiated",
"status_code": "4000",
"status_reason": "INVALID_TRANSACTION_ERROR"
}
func prepareConfig() -> TransactionRequest {
// Get the country code from UserDefaults
let countryCode = "THB"
// Define billing details
let billingDetails = BillingDetails(
billingName: "Test mark",
billingEmail: "markweins@gmail.com",
billingPhone: number ?? "+66900002001",
billingAddress: getBillingAddress()
)
// Define shipping details
let shippingAddress = ShippingAddress(
city: "abc",
countryCode: "TH",
locale: "en",
line1: "address_1",
line2: "address_2",
postalCode: "400202",
state: "Mah"
)
let shippingDetails = ShippingDetails(
shippingName: "xyz",
shippingEmail: "xyz@gmail.com",
shippingPhone: "1234567890",
shippingAddress: shippingAddress
)
// Prepare order details
var orderDetails: [OrderDetails] = []
var totalAmount = 0.0
for details in self.selectedProducts {
let product = OrderDetails(
id: details.id ?? "",
name: details.title ?? "",
price: details.price ?? 0,
quantity: details.quantity ?? 1, // Ensure quantity has a default value
imageUrl: details.imageName ?? ""
)
orderDetails.append(product)
totalAmount += (details.price ?? 0) * Double(details.quantity ?? 1) // Calculate total amount
}
// Apply promo discount and charges
let promoDiscount = 110.0
let charges = 100.0
totalAmount += charges - promoDiscount
// Define merchant details
let merchantDetails = MerchantDetails(
name: "Downy",
logo: "https://upload.wikimedia.org/wikipedia/commons/a/a6/Logo_NIKE.svg",
backUrl: "https://demo.chaiport.io/checkout.html",
promoCode: "Downy350",
promoDiscount: promoDiscount,
shippingCharges: charges
)
// Create transaction request
var transactionRequest = TransactionRequest(
portOneKey: clientKey,
key: clientKey,
merchantDetails: merchantDetails,
paymentChannel: "VNPAY",
paymentMethod: "VNPAY_ALL",
merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))",
amount: Int(totalAmount), // Convert to integer if necessary
currency: countryCode,
signatureHash: "123", // Placeholder for signature hash
billingAddress: billingDetails,
shippingAddress: shippingDetails,
orderDetails: orderDetails,
successURL: "https://test-checkout.chaiport.io/success.html",
failureURL: "https://test-checkout.chaiport.io/failure.html",
redirectURL: "chaiport://checkout",
countryCode: countryCode,
routingEnabled: false,
routingParams: nil,
transactionType: UserDefaults.getTransactionType.code,
bankDetails: nil,
directBankTransferDetails: nil
)
// Generate and set the signature hash
let signatureHash = createSignatureHash(transactionRequest)
transactionRequest.signatureHash = signatureHash
return transactionRequest
}

4. Direct Bank Transfer#

Fetch Direct Bank Transfer Details#

To fetch the Direct Bank Transfer details:

checkout?.fetchDBTDetails(clientKey: "portoneKey", onCompletionHandler: { [weak self] result in
switch result {
case .success(let response):
print("response", response)
case .failure(let error):
print("error", error)
}
})

Process Direct Bank Transfer Payment#

Prepare the payload with DirectBankTransferDetails and initiate the payment:

func prepareConfig(type: PaymentMethod) -> TransactionRequest {
let countryCode = UserDefaults.getCurrency.code
let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "markweins@gmail.com", billingPhone: number ?? "+66900002001", billingAddress: getBillingadress())
let shippingAddress = ShippingAddress(city: "abc", countryCode: "TH", locale: "en", line1: "address_1", line2: "address_2", postalCode: "400202", state: "Mah")
let shippingDetails = ShippingDetails(shippingName: "xyz", shippingEmail: "xyz@gmail.com", shippingPhone: "1234567890", shippingAddress: shippingAddress)
var orderDetails: [OrderDetails] = []
for details in self.selectedProducts {
let product = OrderDetails(id: details.id ?? "", name: details.title ?? "", price: details.price ?? 0, quantity: 1, imageUrl: details.imageName ?? "")
orderDetails.append(product)
totalAmount += (details.price ?? 0) * details.quantity
}
let promoDiscount = 110.0
let charges = 100.0
self.totalAmount = totalAmount + charges - promoDiscount
let merchantDetails = MerchantDetails(name: "Downy", logo: "https://upload.wikimedia.org/wikipedia/commons/a/a6/Logo_NIKE.svg", backUrl: "https://demo.chaiport.io/checkout.html", promoCode: "Downy350", promoDiscount: promoDiscount, shippingCharges: charges)
let directBankTransferDetails = DirectBankTransferDetails(amountPaid: 1123, customerName: "sam", paymentSlip: nil, transactionTime: "2024-04-17T07:39:44.000Z")
var transactionRequest = TransactionRequest(
portOneKey: clientKey,
key: clientKey,
merchantDetails: merchantDetails,
paymentChannel: "VNPAY",
paymentMethod: "VNPAY_ALL",
merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))",
amount: Int(self.totalAmount),
currency: countryCode,
signatureHash: "123",
billingAddress: billingDetails,
shippingAddress: shippingDetails,
orderDetails: orderDetails,
successURL: "https://test-checkout.chaiport.io/success.html",
failureURL: "https://test-checkout.chaiport.io/failure.html",
redirectURL: "chaiport://checkout",
countryCode: countryCode,
routingEnabled: false,
routingParams: nil,
transactionType: UserDefaults.getTransactionType.code,
bankDetails: nil,
directBankTransferDetails: directBankTransferDetails
)
let signatureHash = createSignatureHash(transactionRequest)
transactionRequest.signatureHash = signatureHash
return transactionRequest
}

5. Instalments#

Fetch Bank List for Instalments#

checkout?.postbankDetails(paymentChannel: channelName, bankListBody: bankListBody, subMerchantKey: nil, completionHandler: { [weak self] result in
print("result", result)
switch result {
case .success(let response):
print("data", response)
case .failure(let error):
print("error", error)
}
})

Bank list body params:

  • amount (Double)
  • environment (String)
  • portoneKey (String)
  • isMerchantSponsored (Boolean)
  • paymentMethod (String)
  • overrideDefault (Boolean)
  • currency (String)

Process Instalment Payment#

Prepare the payload with BankDetails and initiate the payment:

func prepareConfig(type: PaymentMethod) -> TransactionRequest {
let countryCode = UserDefaults.getCurrency.code
let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "markweins@gmail.com", billingPhone: number ?? "+66900002001", billingAddress: getBillingadress())
let shippingAddress = ShippingAddress(city: "abc", countryCode: "TH", locale: "en", line1: "address_1", line2: "address_2", postalCode: "400202", state: "Mah")
let shippingDetails = ShippingDetails(shippingName: "xyz", shippingEmail: "xyz@gmail.com", shippingPhone: "1234567890", shippingAddress: shippingAddress)
var orderDetails: [OrderDetails] = []
for details in self.selectedProducts {
let product = OrderDetails(id: details.id ?? "", name: details.title ?? "", price: details.price ?? 0, quantity: 1, imageUrl: details.imageName ?? "")
orderDetails.append(product)
totalAmount += (details.price ?? 0) * details.quantity
}
let promoDiscount = 110.0
let charges = 100.0
self.totalAmount = totalAmount + charges - promoDiscount
let merchantDetails = MerchantDetails(name: "Downy", logo: "https://upload.wikimedia.org/wikipedia/commons/a/a6/Logo_NIKE.svg", backUrl: "https://demo.chaiport.io/checkout.html", promoCode: "Downy350", promoDiscount: promoDiscount, shippingCharges: charges)
let bankDetails = BankDetails(bankName: "Krungsri Bank", bankCode: "installment_bay", isMerchantSponsored: false, instalmentPeriod: InstalmentPeriod(interest: 0.8, month: 4))
var transactionRequest = TransactionRequest(
portOneKey: clientKey,
key: clientKey,
merchantDetails: merchantDetails,
paymentChannel: "VNPAY",
paymentMethod: "VNPAY_ALL",
merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))",
amount: Int(self.totalAmount),
currency: countryCode,
signatureHash: "123",
billingAddress: billingDetails,
shippingAddress: shippingDetails,
orderDetails: orderDetails,
successURL: "https://test-checkout.chaiport.io/success.html",
failureURL: "https://test-checkout.chaiport.io/failure.html",
redirectURL: "chaiport://checkout",
countryCode: countryCode,
routingEnabled: false,
routingParams: nil,
transactionType: UserDefaults.getTransactionType.code,
bankDetails: bankDetails,
directBankTransferDetails: nil
)
let signatureHash = createSignatureHash(transactionRequest)
transactionRequest.signatureHash = signatureHash
return transactionRequest
}

6. Fetch Available Payment Methods#

checkout?.getAvailablePaymentGateways(portOneKey: "Portonekey", currency: "THB", subMerchantKey: nil, completionHandler: { [weak self] result in
print("result", result)
switch result {
case .success(let response):
print("data", response)
case .failure(let error):
print("error", error)
}
})

7. Merchant Centric Card Vault#

Add Customer Card#

checkout?.addCardForCustomerId(customerId: customerId, clientKey: "portoneKey", cardData: self.cardDetails, jwtToken: token, subMerchantKey: nil, onCompletionHandler: { (result) in
switch result {
case .success(let data):
print("data", data)
case .failure(let error):
self.showData(data: "\(error)")
}
})

CardData Example:

CardDetails(
token: "",
cardNumber: "424242******4242",
expiryMonth: "07",
expiryYear: "2031",
cardHolderName: "sam",
type: "visa",
cvv: "123"
)

Delete Customer Card#

checkout?.deleteCardForCustomerId(customerId: customerId, clientKey: clientKey, jwtToken: createJWTToken(), cardData: DeleteCardDataObject(token: token), subMerchantKey: nil, onCompletionHandler: { (result) in
switch result {
case .success(let data):
print("data", data)
case .failure(let error):
print("error", error)
}
})

DeleteCardDataObject Example:

DeleteCardDataObject(token: "cdec91449d3a4b4bae9144d586a2b972")

Fetch All Customer Cards#

checkout?.fetchCustomerCards(customerId: customerId, clientKey: clientKey, jwtToken: token, subMerchantKey: nil, onCompletionHandler: { (result) in
switch result {
case .success(let data):
print("data", data)
case .failure(let error):
print("error", error)
}
})

Here's a detailed guide for implementing failover routing, and pre-authorization and capture payment functionalities using Swift.

8. Failover Routing#

1. Enable Failover Routing in Payment Request#

To enable failover routing, set the following parameters in your payment payload:

  • isRoutingEnabled to true
  • routingParams with type set to "failover" and the routeRef from the merchant portal

Here's how to set these parameters and initiate the payment:

func initiatePaymentWithFailoverRouting() {
var payload = getDefaultPayload()
payload.isRoutingEnabled = true
payload.routingParams = RoutingParams(type: "failover", routeRef: UserDefaults.getRouteRef)
checkout?.initiatePayment(payload, nil) { result in
switch result {
case .success(let data):
// Handle success
print("data", data)
case .failure(let error):
// Handle error
print("error", error)
}
}
}

2. Fetch List of Routes#

To fetch the list of routes created in the merchant portal, use the fetchRoutes method:

func fetchRoutes() {
checkout?.fetchRoutes(clientKey: clientKey, jwtToken: token, subMerchantKey: nil) { result in
switch result {
case .success(let data):
// Process fetched routes
print("data", data)
case .failure(let error):
// Handle error
print("error", error)
}
}
}

9. Pre-Authorization and Capture Payment#

1. Implement Pre-Authorization#

To set up a pre-authorization, adjust the transactionType in your payload:

func preparePreAuthPayload() -> TransactionRequest {
var payload = getDefaultPayload()
payload.transactionType = "PREAUTH"
return payload
}

2. Capture Payment#

To capture a payment, use the captureTransactionAPI method with the necessary parameters:

func capturePayment(transactionOrderRef: String, jwtToken: String) {
checkout?.captureTransactionAPI(transactionOrderRef: transactionOrderRef, clientKey: UserDefaults.getPortoneKey!, jwtToken: jwtToken, subMerchantKey: nil) { result in
switch result {
case .success(let response):
// Handle success
print("data", response)
case .failure(let error):
// Handle error
print("error", error)
}
}
}

Payload Structure#

To successfully create a payment request, the payload must include various parameters organized as follows:

1. Web Checkout Request Parameters:

ParameterData TypeDescription
portOneKeyStringMandatory key for authentication
merchantDetailsMerchantDetailsDetails about the merchant
merchantOrderIdStringMandatory unique order identifier
signatureHashStringMandatory security hash for the request
amountDoubleMandatory total amount for the transaction
currencyStringMandatory currency code (e.g., USD)
countryCodeStringMandatory country code (e.g., US)
billingDetailsBillingDetailsOptional billing information
shippingDetailsShippingDetailsOptional shipping information
orderDetails[OrderDetail]Optional array of order details
successUrlStringMandatory URL to redirect on successful payment
failureUrlStringMandatory URL to redirect on failed payment
expiryHoursIntMandatory expiry time for the transaction in hours
sourceStringMandatory source of the payment request
descriptionStringOptional description of the transaction
showShippingDetailsBooleanOptional flag to show shipping details
showBackButtonBooleanOptional flag to show a back button
defaultGuestCheckoutBooleanOptional flag for default guest checkout
isCheckoutEmbedBooleanOptional flag if the checkout is embedded
redirectUrlStringMandatory URL to redirect after checkout
environmentStringMandatory environment type (sandbox or live)
bankDetailsBankDetailsOptional bank details for payment
directBankTransferDirectBankTransferDetailsOptional direct bank transfer details

2. MerchantDetails:

ParameterData TypeDescription
nameStringOptional merchant name
logoStringOptional URL to merchant's logo
backUrlStringOptional URL to redirect back
promoCodeStringOptional promotional code
promoDiscountIntOptional promotional discount
shippingChargesDoubleOptional shipping charges

3. ShippingDetails:

ParameterData TypeDescription
shippingNameStringOptional shipping recipient name
shippingEmailStringOptional shipping recipient email
shippingPhoneStringOptional shipping recipient phone
shippingAddressAddressOptional shipping address

4. BillingDetails:

ParameterData TypeDescription
billingNameStringOptional billing name
billingEmailStringOptional billing email
billingPhoneStringOptional billing phone
billingAddressAddressOptional billing address

5. Address:

ParameterData TypeDescription
cityStringOptional city
countryCodeStringOptional country code
localeStringOptional locale
line1StringOptional address line 1
line2StringOptional address line 2
postalCodeStringOptional postal code
stateStringOptional state

6. DirectBankTransferDetails:

ParameterData TypeDescription
customerNameStringMandatory customer name
transactionTimeStringMandatory transaction time
amountPaidDoubleMandatory amount paid

7. BankDetails:

ParameterData TypeDescription
bankNameStringMandatory bank name
bankCodeStringMandatory bank code
isMerchantSponsoredBoolMandatory flag for merchant-sponsored bank
instalmentPeriodInstalmentPeriodMandatory instalment details

8. InstalmentPeriod:

ParameterData TypeDescription
monthIntMandatory number of months
interestDoubleMandatory interest rate

9. OrderDetail:

ParameterData TypeDescription
idStringOptional order ID
priceDoubleOptional price of the item
nameStringOptional name of the item
quantityIntOptional quantity
imageStringOptional image URL

Probable Errors#

Pass the viewController which is embedded in navigation controller to checkout delegate#

  1. If the passed view controller does not have any navigation by default, the top view controller will be taken from the application's shared windows.

INVALID_UNAUTHORIZED_JWT_TOKEN_ERROR#

  1. Check whether PortOne Key and the Secret Key are from the same account.
  2. Check whether the Secret Key has not been modified.
  3. Check whether the Bearer keyword is added before the generated token with a space. Format: Bearer $jwtToken.
  4. Verify if the expiration time is greater than the current time.

INVALID_UNAUTHORISED_TRANSACTION_SIGNATURE_ERROR#

  1. Check whether all parameters match with the payload/request.
  2. Check whether the portone key matches with the payload and the account.

INVALID_UNAUTHORISED_TRANSACTION_IAMPORTKEY_ERROR#

  1. Check whether the portone key matches with the payload and the account.

INVALID_PAYMENT_CHANNEL#

  1. Make sure the payment channels and payment methods added in the payload are enabled from the PortOne portal.

INVALID_ENVIRONMENT#

  1. Ensure you have specified the environment as either sandbox or live.

Summation of order value, tax, duties, shipping, and discount is equal to amount#

  1. If items are provided, verify that the values match the total amount: sum(items price * items quantity) + shipping charge - discount = amount.
  2. Mandatory parameters in payload:
    • price
    • promo_discount (0 is acceptable)
    • shipping_charges (0 is acceptable)