PAYATHON 2026

iOS 集成 AliPay 应该从哪里开始?

支付阿杰

结论

iOS 集成支付宝时,应从支付宝开放平台对应产品的官方文档和 iOS SDK 入手,不要直接套用网页支付接口文档。

通常的接入流程如下:

  1. 在支付宝开放平台创建应用,并申请所需的支付产品。
  2. 服务端生成支付订单,使用商户私钥完成签名。
  3. iOS App 获取服务端返回的完整 orderString。
  4. App 调用支付宝 iOS SDK 发起支付。
  5. 服务端接收支付宝异步通知,验签通过后更新订单状态。

问题中提到的 globalprod.alipay.com/order/integrationGuide.htm 主要面向跨境或网页支付接口。它是否适合当前业务,要看商户签约主体、用户所在地区以及已经开通的支付产品。中国大陆支付宝开放平台、Alipay+ 和国际支付宝商户平台的产品、SDK 与接口不能混用。

集成前确认业务类型

写代码前,先确认以下信息:

  • 商户签约主体属于哪个国家或地区。
  • 用户使用中国大陆支付宝,还是海外钱包或 Alipay+。
  • 支付发生在原生 App、移动网页还是服务端。
  • 商户后台实际开通了哪个支付产品。
  • 支付宝后台是否已经审核并启用对应应用。

如果服务对象是中国大陆支付宝用户,通常应查找支付宝开放平台的「App 支付」产品及其 iOS SDK。

如果使用国际支付宝、Alipay+ 或跨境收单,则应以商户后台提供的产品文档为准。即使接口名称相似,网关地址、签名规则、请求参数和客户端 SDK 也可能不同。

推荐的系统结构

不要在 iOS 客户端组装和签名支付请求。客户端与服务端可以按下面的方式分工:

iOS App
   │  创建业务订单
   ▼
商户服务端
   │  检查金额、库存和订单状态
   │  生成支付宝请求参数
   │  使用商户私钥签名
   ▼
iOS App
   │  获取完整 orderString
   │  调用 AlipaySDK
   ▼
支付宝客户端 / 支付页面
   │
   ├── 返回客户端支付结果
   └── 异步通知商户服务端

商户私钥只能保存在服务端。把私钥、签名逻辑或可修改的订单金额放进 App,会增加密钥泄露和订单篡改的风险。

服务端生成支付订单

服务端应使用支付宝提供的服务端 SDK 构造 App 支付请求。下面的代码只说明职责和数据流,具体类名与参数应以所选平台当前版本的官方 SDK 为准:

// 示例仅展示职责和数据流
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(order.getOrderNo());
model.setSubject(order.getTitle());
model.setTotalAmount(order.getAmount().toPlainString());
model.setProductCode("QUICK_MSECURITY_PAY");

request.setBizModel(model);
request.setNotifyUrl("https://api.example.com/payments/alipay/notify");

AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

// 将完整 orderString 返回给 iOS,不要只返回 biz_content
String orderString = response.getBody();

服务端至少要完成以下校验:

  • out_trade_no 由服务端生成,并且必须唯一。
  • total_amount 取自服务端订单,不能信任客户端传入的金额。
  • 回调地址使用可公开访问的 HTTPS 地址。
  • 相同订单的重复请求需要进行幂等处理。
  • 日志中不得记录商户私钥、完整用户信息或其他敏感数据。

iOS 安装并配置 SDK

iOS SDK 的安装方式可能因支付宝平台和 SDK 版本而异。应从商户后台或官方文档下载 SDK,再根据接入说明选择 CocoaPods、Swift Package Manager 或手动集成。

原生 App 通常还要配置 URL Scheme,以便支付结束后返回应用。例如:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.example.payment</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>examplealipay</string>
        </array>
    </dict>
</array>

examplealipay 只是示例。实际使用的 Scheme 必须在应用内保持一致,并尽量避免与其他 App 冲突。

部分新版 SDK 或特定平台可能还要求配置 Universal Link。具体要求取决于 SDK 版本,不能只参考旧示例,应以下载 SDK 时附带的接入说明为准。

iOS 调起支付

下面是常见的 Swift 调用方式。支付宝 iOS SDK 提供的 Objective-C API 通常可以通过桥接在 Swift 中直接使用:

import UIKit
import AlipaySDK

final class PaymentService {
    func pay(orderString: String) {
        let appScheme = "examplealipay"

        AlipaySDK.defaultService().payOrder(
            orderString,
            fromScheme: appScheme
        ) { result in
            guard let result else {
                print("No payment result")
                return
            }

            let resultStatus = result["resultStatus"] as? String
            let memo = result["memo"] as? String

            print("resultStatus: \(resultStatus ?? "unknown")")
            print("memo: \(memo ?? "")")

            // 客户端结果只用于更新界面。
            // 最终支付状态应向商户服务端查询。
        }
    }
}

调用 SDK 前,先从商户服务端获取 orderString:

func createAndPay() async throws {
    let url = URL(string: "https://api.example.com/orders/123/alipay")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    let (data, response) = try await URLSession.shared.data(for: request)

    guard let httpResponse = response as? HTTPURLResponse,
          (200...299).contains(httpResponse.statusCode) else {
        throw PaymentError.createOrderFailed
    }

    let payload = try JSONDecoder().decode(PaymentPayload.self, from: data)

    await MainActor.run {
        PaymentService().pay(orderString: payload.orderString)
    }
}

struct PaymentPayload: Decodable {
    let orderString: String
}

enum PaymentError: Error {
    case createOrderFailed
}

如果当前 SDK 要求应用显式处理返回 URL,可以根据项目使用的生命周期实现相应入口。下面是使用 SceneDelegate 的示例:

func scene(
    _ scene: UIScene,
    openURLContexts URLContexts: Set<UIOpenURLContext>
) {
    guard let url = URLContexts.first?.url else {
        return
    }

    AlipaySDK.defaultService().processOrder(
        withPaymentResult: url
    ) { result in
        print("Alipay callback: \(result ?? [:])")
    }
}

不同版本的 SDK 可能使用不同的回调方法,也可能增加 Universal Link 参数。如果当前方法签名与示例不同,应查阅 SDK 头文件和官方示例工程,不要为了兼容旧代码而降级 SDK。

服务端处理异步通知

客户端显示「支付成功」,不等于商户已经可靠收到款项。App 可能被关闭,网络也可能中断,而且客户端返回值不能作为可信的记账依据。

服务端收到支付宝通知后,应按以下步骤处理:

  1. 使用支付宝公钥或平台证书验证通知签名。
  2. 检查通知中的应用身份和商户身份。
  3. 确认 out_trade_no 对应的订单存在。
  4. 比较通知中的金额、币种与本地订单是否一致。
  5. 确认交易状态属于业务认可的成功状态。
  6. 以幂等方式更新订单。
  7. 按平台要求返回成功应答。

伪代码如下:

if not verifySignature(notification):
    reject

order = findOrder(notification.out_trade_no)

if order does not exist:
    reject

if notification.amount != order.amount:
    reject

if notification.currency != order.currency:
    reject

if notification indicates successful payment:
    markOrderPaidIdempotently(order)

return requiredSuccessResponse

iOS 收到支付回调后,可以向商户自己的服务端查询订单状态:

func refreshOrderStatus(orderID: String) async throws -> OrderStatus {
    // 调用商户服务端查询本地订单状态。
    // 如果异步通知尚未到达,可由服务端按需调用支付宝查询接口核实。
    fatalError("Implement with your backend API")
}

常见问题

找不到适用于 iOS 的步骤

一般是因为当前打开的是网页支付、收单网关或旧版国际站文档。先进入商户实际签约的平台,再从已经开通的产品中查找 App 支付的客户端 SDK 和服务端 API。

SDK 能拉起支付宝,但无法返回 App

可以依次检查:

  • Info.plist 是否已经配置 URL Scheme。
  • 调用支付时传入的 Scheme 是否与配置完全一致。
  • AppDelegate 或 SceneDelegate 是否正确处理返回 URL。
  • Universal Link 是否按照当前 SDK 的要求配置。
  • 真机上是否安装了支付宝客户端。

客户端返回成功,服务端订单仍未支付

异步通知可能还没有送达,也可能因验签失败而未被处理。应检查服务端回调日志,并通过支付宝交易查询接口核实支付状态,不能直接相信客户端结果。

可以完全在 iOS 中完成支付吗

不建议这样做,通常也无法保证安全。订单签名、私钥管理、金额确认、异步通知验签和最终记账都应由服务端完成。

注意事项

  • 不要把商户私钥打包进 iOS App。
  • 不要让客户端决定最终支付金额。
  • 不要根据客户端回调直接发货或充值。
  • 不要混用测试环境和正式环境的应用标识、密钥、证书与网关配置。
  • 支付宝公钥、应用公钥和商户私钥是不同的概念。
  • 使用证书模式时,按照官方要求管理应用证书、支付宝公钥证书和根证书。
  • SDK 接口可能随版本调整。集成时要同时核对官方示例工程、头文件和商户后台文档。
  • 上线前至少测试支付成功、取消、失败、重复支付、网络中断、App 被杀死、异步通知重试和订单金额不匹配等场景。

备注:内容仅供参考。