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

Nhảy tới nội dung

iOS Native SDK - Embed(V3)

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>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)
    }
    }

6. Checkout Using Web (v3)#

PortOne's Checkout offers a streamlined process by handling the payment UI within the SDK.

  1. Initiate Checkout:

    let token = createJWTToken()
    let payload = prepareConfig()
    checkout?.checkOutUI(config: payload, jwtToken: token, onCompletionHandler: { (result) in
    switch result {
    case .success(let data):
    print("Data", data)
    case .failure(let error):
    print("Error", error)
    }
    })
  2. Prepare Payload:

    func prepareWebConfig() -> WebTransactionRequest {
    let selectedProducts = [
    ProductDetailsObject(id: "6BmAKc", title: "Sririri Toes", description: "Special Design", price: 1, currency: self.CURRENCY, imageName: "https://demo.portone.cloud/images/bella-toes.jpg"),
    ProductDetailsObject(id: "6BmAKcbc", title: "Chikku Loafers", description: "Special Design", price: 15000, currency: self.CURRENCY, imageName: "https://demo.portone.cloud/images/chikku-loafers.jpg")
    ]
    let billingAddress = BillingAddress(city: self.CURRENCY, countryCode: "VN", locale: "en", line1: "address1", line2: "address2", postalCode: "400202", state: "Mah")
    var charges = 110.0
    var promoDiscount = 100.0
    let merchantDetails = MerchantDetails(name: "Downy", logo: "https://upload.wikimedia.org/wikipedia/commons/a/a6/Logo_NIKE.svg", backUrl: "https://demo.portone.cloud/checkout.html", promoCode: "Downy350", promoDiscount: Int(promoDiscount), shippingCharges: charges)
    let billingDetails = BillingDetails(billingName: "Test mark", billingEmail: "markweins@gmail.com", billingPhone: "9913379694", 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)
    var orderDetails: [OrderDetails] = []
    var totalAmount = 0.0
    for details in selectedProducts {
    let product = OrderDetails(id: details.id ?? "", name: details.title ?? "", price: details.price ?? 0, quantity: 1, imageUrl: details.imageName ?? "")
    orderDetails.append(product)
    totalAmount += (product.price ?? 1) * Double(product.quantity ?? 1)
    }
    let transactionAmount = totalAmount + charges - promoDiscount
    var transactionRequest = WebTransactionRequest(portOneKey: self.PORTONE_KEY, merchantDetails: merchantDetails, merchantOrderId: "MERCHANT\(Int(Date().timeIntervalSince1970 * 1000))", amount: transactionAmount, currency: self.CURRENCY, 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: "portone://checkout", countryCode: "VN", expiryHours: 2, source: "mobile", description: "test dec", showShippingDetails: true, showBackButton: false, defaultGuestCheckout: false, isCheckoutEmbed: false, environment: .sandbox)
    let signatureHash = createWebHash(transactionRequest)
    transactionRequest.signatureHash = signatureHash
    return transactionRequest

}

3. **Handle Web View Response**:
```swift
extension AppDelegate: CheckoutDelegate {
func transactionErrorResponse(_ error: Error?) {
print("Error", error)
}
var viewController: UIViewController? {
return AppDelegate.shared.window?.rootViewController
}
func transactionResponse(_ webViewResponse: WebViewResponse?) {
NotificationCenter.default.post(name: NSNotification.Name("webViewResponse"), object: webViewResponse)
print("Webview response", webViewResponse)
}
}
  1. Sample Response:

    • Success:

      {
      "merchant_order_ref": "MERCHANT1630665361511",
      "message": "",
      "is_success": "true",
      "order_ref": "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
      "deep_link": "",
      "channel_order_ref": "1xcrkpVPNq5vuqQDe3eqrHD3OcG",
      "additional_data": null,
      "redirect_url": ""
      }
    • Failed:

      {
      "chaipay_order_ref": "1wa0choxhAy2QtE9ix8aNt8T3Mf",
      "channel_order_ref": "0",
      "merchant_order_ref": "MERCHANT1628681469666",
      "status": "Initiated",
      "status_code": "4000",
      "status_reason": "INVALID_TRANSACTION_ERROR"
      }

7. Error Handling#

  • INVALID_UNAUTHORIZED_JWT_TOKEN_ERROR: Ensure the PortOne key and secret key are correct, and that the JWT token is properly formatted with the Bearer prefix.
  • INVALID_UNAUTHORISED_TRANSACTION_SIGNATURE_ERROR: Verify that all parameters match the payload and the PortOne key is correct.
  • INVALID_PAYMENT_CHANNEL: Confirm that the payment channels and methods are enabled in the PortOne portal.
  • INVALID_ENVIRONMENT: Ensure the environment is set to either sandbox or live.
  • Summation of Order Values: Check that the order value, taxes, duties, shipping, and discounts match the specified amount.

8. JWT Token Generation#

  1. Algorithm: HS256

  2. Generate JWT Token:

    var payload = {
    iss: 'CHAIPAY',
    sub: 'lzrYFPfyMLROallZ', // Portone Key
    iat: new Date().getTime(),
    exp: new Date().getTime() + 100 * 1000,
    };
    var secretKey = " " // Provide secret key for the account
    • iss: Issuer (Default: "PORTONE")
    • sub: Portone Key
    • iat: Issued At (Current timestamp)
    • exp: Expiration Time (Timestamp + 100 seconds)

For more details, refer to the Native iOS - Payment SDK: v2 documentation