1. 后端(PHP):创建服务端接口,用于接收 iOS 请求,生成支付宝支付参数,并处理支付宝的异步通知。
  2. 前端(iOS):在 iOS App 中调用后端接口,获取支付参数,并使用官方 SDK 调起支付宝 App 支付。
  3. 调试与上线:签名验证、沙箱环境测试、以及上线前的注意事项。

第一步:准备工作

在开始编码之前,你必须在支付宝开放平台完成以下配置:

php ios支付宝接口开发教程
(图片来源网络,侵删)
  1. 注册开发者账号:访问 支付宝开放平台 并注册成为开发者。
  2. 创建应用
    • 登录后,进入“开发者中心” -> “应用列表” -> “创建应用”。
    • 选择应用类型为 移动端 App
    • 填写应用名称、图标等信息。
  3. 获取应用信息
    • 应用创建成功后,进入应用详情页。
    • 在“应用信息”页面,你会找到最重要的两个参数:
      • APPID:你的应用唯一标识。
      • 应用私钥 (App Private Key):系统会自动生成一个,请务必妥善保管,并下载 .pem 文件。
    • 在“接口加签方式”页面,确认并设置为 MD5(这是目前最主流的方式,教程基于此)。
  4. 生成支付宝公钥 (Alipay Public Key)
    • 在“接口加签方式”页面,点击“生成支付宝公钥”。
    • 使用你上一步下载的 应用私钥,通过支付宝官方提供的工具(或 OpenSSL 命令)生成支付宝公钥。
    • 将生成的 支付宝公钥 复制并粘贴到页面的输入框中,并保存。
    • 重要:请确保这里填入的是支付宝公钥,而不是你的应用私钥。
  5. 开通产品
    • 在“产品与开发” -> “我的产品”页面,找到并开通 App 支付 产品。
  6. 配置密钥
    • 你需要一个 商户私钥 和一个 支付宝公钥,支付宝会用它的公钥来验证你发送过来的请求是否由你的私钥签名,你也需要用支付宝公钥来验证它发送给你的异步通知是否合法。
    • 你可以将 应用私钥 作为你的 商户私钥 来使用。
  7. 沙箱环境
    • 在“沙箱环境”页面,开启沙箱功能,在开发测试阶段,所有的支付都会在沙箱环境中进行,不会使用真实资金,你需要使用沙箱环境的账号(买家和卖家)进行测试。

第二步:后端开发(PHP)

我们将创建两个核心文件:

  • pay.php:接收 iOS 请求,生成支付参数。
  • notify.php:接收支付宝的异步通知,处理支付结果。

1 安装支付宝 PHP SDK

推荐使用 Composer 来管理 SDK,在你的项目根目录下运行命令:

composer require alipaysdk/easysdk

这会自动下载 SDK 并生成 vendor 目录和 autoload.php 文件。

2 创建配置文件 config.php

<?php
// config.php
return [
    'app_id'      => '你的APPID',
    'notify_url'  => 'https://你的域名/notify.php', // 异步通知地址,必须是公网可访问的URL
    'return_url'  => 'https://你的域名/success.html', // 同步跳转地址(App支付一般不用,但可以配置)
    'merchant_private_key' => file_get_contents(__DIR__ . '/app_private_key.pem'), // 你的应用私钥文件内容
    'alipay_public_key'    => file_get_contents(__DIR__ . . '/alipay_public_key.pem'), // 支付宝公钥文件内容
    'sandbox'     => true, // 是否为沙箱环境
];

注意:将 app_private_key.pemalipay_public_key.pem 这两个文件放在与 config.php 同级的目录下,并将内容填入。

php ios支付宝接口开发教程
(图片来源网络,侵删)

3 创建支付接口 pay.php

这个文件会被 iOS App 调用。

<?php
// pay.php
require_once 'vendor/autoload.php';
require_once 'config.php';
use EasyAlipay\Payment\PagePay;
// 1. 获取客户端传递的订单信息
// 在实际项目中,这里应该从数据库中创建订单,并获取订单号和金额
$out_trade_no = 'PHP' . date('YmdHis') . rand(1000, 9999); // 商户订单号,保证唯一
$total_amount = 0.01; // 订单金额,单位:元
// 2. 初始化SDK
$config = new \EasyAlipay\Payment\Config();
$config->setAppId($config_arr['app_id']);
$config->setNotifyUrl($config_arr['notify_url']);
$config->setReturnUrl($config_arr['return_url']);
$config->setMerchantPrivateKey($config_arr['merchant_private_key']);
$config->setAlipayPublicKey($config_arr['alipay_public_key']);
if ($config_arr['sandbox']) {
    $config->setMode('dev'); // 沙箱环境
} else {
    $config->setMode('prod'); // 生产环境
}
$pay = new PagePay($config);
// 3. 构建请求参数
$bizContent = [
    'out_trade_no' => $out_trade_no,
    'total_amount' => $total_amount,
    'subject'      => '测试商品', // 商品标题
    'product_code' => 'QUICK_MSECURITY_PAY', // App支付固定值
];
try {
    // 4. 调用SDK,生成支付参数
    $response = $pay->pay($bizContent);
    // 5. 将支付参数返回给iOS客户端
    // $response->body 是一个JSON字符串,包含 app_id, string, sign 等字段
    $paymentParams = json_decode($response->body, true);
    if ($paymentParams && isset($paymentParams['alipay_trade_wap_pay_response'])) {
        // 将完整的响应体直接返回给iOS
        echo json_encode([
            'status' => 'success',
            'data'   => $paymentParams
        ]);
    } else {
        throw new \Exception('生成支付参数失败');
    }
} catch (\Exception $e) {
    // 返回错误信息
    echo json_encode([
        'status' => 'error',
        'msg'    => $e->getMessage()
    ]);
}

4 创建异步通知处理 notify.php

支付宝在支付完成后,会向这个地址发送一个 POST 请求,以通知支付结果。这是最关键的一步,必须验证通知的合法性!

<?php
// notify.php
require_once 'vendor/autoload.php';
require_once 'config.php';
use EasyAlipay\Payment\PagePay;
// 1. 初始化SDK(与pay.php相同)
$config = new \EasyAlipay\Payment\Config();
// ... (这里省略了与pay.php中相同的配置代码)
$pay = new PagePay($config);
// 2. 获取支付宝POST过来的异步通知数据
$params = $_POST;
// 3. 验证签名(至关重要!)
// SDK提供了验签方法
$verifyResult = $pay->verifyNotify($params);
if ($verifyResult) {
    // 4. 验证成功,处理业务逻辑
    // 4.1 验证业务参数
    $out_trade_no = $params['out_trade_no']; // 商户订单号
    $trade_no = $params['trade_no'];         // 支付宝交易号
    $trade_status = $params['trade_status']; // 交易状态
    // 4.2 根据交易状态处理
    if ($trade_status == 'TRADE_SUCCESS' || $trade_status == 'TRADE_FINISHED') {
        // TODO: 在这里编写你的业务逻辑
        // 更新数据库中的订单状态为“已支付”
        // $order = Order::find($out_trade_no);
        // $order->status = 'paid';
        // $order->save();
        // 业务处理成功,告诉支付宝我已收到通知
        echo 'success'; // 必须是 success 字符串
    } else {
        // 其他状态,也可以记录日志
        file_put_contents('notify.log', date('Y-m-d H:i:s') . " - Unhandled status: {$trade_status}\n", FILE_APPEND);
        echo 'success';
    }
} else {
    // 5. 验证失败
    // 记录错误日志,通知技术人员
    file_put_contents('notify.log', date('Y-m-d H:i:s') . " - Verify failed!\n" . print_r($params, true) . "\n", FILE_APPEND);
    echo 'fail'; // 告诉支付宝验签失败
}

第三步:前端开发(iOS)

1 集成支付宝 SDK

  1. 使用 CocoaPods (推荐): 在你的 Podfile 中添加:

    pod 'AlipaySDK-iOS'

    然后在终端运行 pod install

    php ios支付宝接口开发教程
    (图片来源网络,侵删)
  2. 手动集成: 从 支付宝开放平台 下载最新的 AlipaySDK-iOS.zip,将 AlipaySDK.framework 拖入你的 Xcode 项目。

2 配置 URL Types

在 Xcode 中,选择你的项目 -> Target -> Info -> URL Types,添加一个 URL Type。

  • URL Schemes:填入你在支付宝开放平台创建应用时填写的 Bundle Id,这用于 App 间跳转回来。
  • Role:选择 Viewer

3 编写支付代码

假设你有一个 PaymentManager 来处理支付逻辑。

// PaymentManager.swift
import Foundation
import AlipaySDK
class PaymentManager: NSObject {
    // 单例模式
    static let shared = PaymentManager()
    private override init() {}
    // 调用支付
    func requestPayment(orderInfo: [String: Any], in viewController: UIViewController) {
        // 1. 调用你的后端接口,获取支付参数
        // 这里使用 URLSession 发送一个 POST 请求
        guard let url = URL(string: "https://你的域名/pay.php") else { return }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: orderInfo, options: [])
        } catch {
            print("Error creating JSON body: \(error)")
            return
        }
        let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            guard let self = self else { return }
            if let error = error {
                print("Network error: \(error)")
                DispatchQueue.main.async {
                    self.showAlert(message: "网络请求失败: \(error.localizedDescription)")
                }
                return
            }
            guard let data = data else { return }
            do {
                if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
                   let status = json["status"] as? String,
                   status == "success",
                   let paymentData = json["data"] as? [String: Any] {
                    // 2. 调起支付宝支付
                    DispatchQueue.main.async {
                        self.startAlipayPayment(paymentData: paymentData, in: viewController)
                    }
                } else {
                    let errorMsg = json["msg"] as? String ?? "支付参数获取失败"
                    DispatchQueue.main.async {
                        self.showAlert(message: errorMsg)
                    }
                }
            } catch {
                print("JSON parsing error: \(error)")
                DispatchQueue.main.async {
                    self.showAlert(message: "数据解析失败")
                }
            }
        }
        task.resume()
    }
    // 调起支付宝
    private func startAlipayPayment(paymentData: [String: Any], in viewController: UIViewController) {
        // 从后端返回的数据中,取出 orderString
        // 这个 orderString pay.php 中 $paymentParams['alipay_trade_wap_pay_response']['body']
        // 但更常见的做法是,后端直接返回一个完整的、可以直接用于调支付的字符串
        // 这里假设后端返回的 paymentData 中有一个 key 为 "orderString"
        guard let orderString = paymentData["orderString"] as? String ?? paymentData["body"] as? String else {
            showAlert(message: "无效的支付参数", in: viewController)
            return
        }
        // 调用 AlipaySDK
        AlipaySDK.defaultService()?.payOrder(orderString, fromScheme: "你的BundleId") { resultDic, error in
            if error != nil {
                // 支付失败
                print("支付失败: \(error?.localizedDescription ?? "未知错误")")
                self.showAlert(message: "支付失败", in: viewController)
            } else {
                // resultDic 是一个字典,包含 resultStatus, result, memo
                if let result = resultDic?["result"] as? String {
                    // 解析 result 字符串
                    let resultStatus = result.components(separatedBy: "&").first?.components(separatedBy: "=").last ?? ""
                    switch resultStatus {
                    case "9000":
                        // 支付成功
                        print("支付成功")
                        self.showAlert(message: "支付成功", in: viewController)
                        // TODO: 通知服务器查询最终支付结果(因为用户可能取消支付,但异步通知还没到)
                        self.queryOrderStatus(outTradeNo: "你的订单号") // 你需要在这里传入正确的订单号
                    case "6001":
                        // 用户中途取消
                        print("用户取消支付")
                        // 不需要弹窗,用户主动取消
                    case "4000", "6002", "6004":
                        // 支付失败
                        print("支付失败")
                        self.showAlert(message: "支付失败,请重试", in: viewController)
                    default:
                        // 其他情况
                        print("支付结果: \(result)")
                        self.showAlert(message: "支付结果未知", in: viewController)
                    }
                }
            }
        }
    }
    // 查询订单状态(推荐)
    private func queryOrderStatus(outTradeNo: String) {
        // 这里调用你后端的一个查询接口,让后端去支付宝服务器查询最新的交易状态
        // 因为 App 端收到的 resultStatus 可能不准确
        // ... 实现网络请求 ...
    }
    // 弹窗提示
    private func showAlert(message: String, in viewController: UIViewController) {
        let alert = UIAlertController(title: "提示", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "确定", style: .default))
        viewController.present(alert, animated: true)
    }
}

4 在你的业务逻辑中调用

// 在某个按钮的点击事件中
@IBAction func payButtonTapped(_ sender: UIButton) {
    let orderInfo: [String: Any] = [
        "order_id": "IOS123456",
        "amount": "0.01",
        "subject": "测试商品"
    ]
    PaymentManager.shared.requestPayment(orderInfo: orderInfo, in: self)
}

第四步:调试与上线

  1. 沙箱测试

    • 确保你的 PHP 配置中 sandbox 设置为 true
    • 在 iOS 项目中,确保 Bundle Id 和沙箱环境配置一致。
    • 使用支付宝沙箱账号(买家)登录你的 App,进行支付测试。
    • 支付成功后,检查你的 notify.php 是否被正确调用,并检查数据库中的订单状态是否更新。
  2. 生产环境发布

    • 修改配置:将 config.php 中的 sandbox 设置为 false
    • 重新签名:确保你的 App 在发布时使用了正确的 Provisioning Profile 和 Certificates。
    • 提交审核:将你的 App 提交到 App Store。
    • 配置正式环境:在支付宝开放平台,将应用切换到“生产环境”,并确保所有配置(如密钥、公钥)都已更新为生产环境的。
  3. 常见问题

    • -1003: AlipaySDK fail to encode the order string:后端生成的 orderString 格式不正确,检查 PHP 端是否正确调用了 SDK 并返回了标准格式。
    • -1001: ... not registered in alipay:App 的 Bundle Id 和支付宝开放平台填写的 Bundle Id 不一致。
    • 6001: User cancelled:这是正常情况,表示用户在支付宝 App 中取消了支付。
    • 异步通知没收到
      • 检查 notify_url 是否是公网可访问的 HTTPS 地址(HTTP 在生产环境会被支付宝拒绝)。
      • 检查服务器防火墙是否阻止了来自支付宝服务器的请求。
      • 检查 notify.php 中的验签逻辑是否正确。
      • 在支付宝开放平台的应用详情页,可以配置“异步通知验签方式”,确保和代码中一致。

这份教程涵盖了从申请账号、后端接口开发、iOS SDK 集成到最终上线测试的全过程,请务必牢记以下几个核心要点:

  • 安全第一:永远不要相信客户端传来的任何金额和订单信息,所有业务逻辑和状态变更必须由服务端在验证了支付宝的异步通知后完成。
  • 签名验证:在 notify.php 中对支付宝的签名进行严格验证,这是防止伪造通知的关键。
  • 沙箱测试:在发布到生产环境前,务必在沙箱环境中完成所有流程的测试。
  • 查询订单:不要完全依赖 App 端返回的支付结果,最好的做法是 App 支付成功后,由服务端主动向支付宝查询最终的交易状态。

祝你开发顺利!