写一个支付宝
2017-12-15
前言
之前一直在做卖线上课程的业务,前期为了尽快上线 + 多赚钱 + 心存侥幸就用了和电商实物商品一样的支付渠道,果不其然,跑了小一年后 被果子一封远洋邮件警告,虚拟商品需要走 IAP 内购,不准使用其他支付方式,所以就得快马加鞭赶紧设计一套 IAP 支付逻辑出来。
IAP概念
IAP 内购是果子对在 iOS 平台上进行的虚拟商品交易所设计的一套规定(可能是因为虚拟商品交易是一本万利,谁都眼红),虚拟商品主要分为五个大类:
- Consumable Products (消耗品):比如游戏内金币,跨设备无法通过苹果恢复
- Non-consumable products (非消耗品):一次购买,终身可用,比如 app 内滤镜这种,可垮设备
- Auto-renewable subscriptions (自动更新订阅品):需要在苹果那边配置 如年付费的数字杂志
- Non-renewable subscriptions (非自动更新订阅品):和三的唯一区别是信息存放在自己服务器上
- Free subscriptions (免费订阅品)
设计
一般实现
每个虚拟物品创建需要去 iTunes Connect 创建一个对应的购买项目,比如课程对应是 非消耗品
,用户购买课程,其他设备可通过苹果接口恢复购买。但是苹果有自己的定价指导,每次购买价格基本是固定的,不太好做定价和后期的打折促销。
主流的方案一般是在 app 内引入 虚拟货币
的概念,用户通过 AppStore 充值购买 app 内的虚拟货币,来购买课程,我们这边只用在 iTunes Connect 创建固定数量的虚拟货币购买项目,数据的同步由我们服务器提供。
IAP 流程
上面👆是果子官方给的 IAP 购买流程指导:
- 请求获取内购列表
- App Store 返回内购列表
- App 展示内购列表
- 用户选择内购
- 发送购买请求
- App Store 处理购买请求
- 取消/发放内购
当然这个流程坑还是很多的,只适用于无账号体系/使用 Apple iCloud 账号体系的 App,查了下一般符合国情的流程是这样的:
- 请求获取内购列表
- 应用服务器返回内购列表及对应苹果端的 Product ID
- 客户端验证 Product ID 并展示
- 用户选择内购
- 向自己服务器发送购买请求并生成订单
- 客户端带着订单号向 App Store 发送购买请求
- App Store 返回购买凭证
- 向自己服务器发送凭证请求验证
- 自己服务器向 Apple 服务器发送验证请求
- 验证成功后发放内购物品
服务
流程有了,现在就来看下这个服务需要做什么
- 有一个账号体系:余额归属
- 支持多个 appID:买课币,买书币等等
- 加减余额
- 消费明细
这不就是一个支付宝么,购买 IAP 商品相当于充钱变成xx币,然后使用这个xx币在 app 内消费,所以相当于我们自己搞一个类似支付宝的东西作为一个新的支付渠道, 这样之前的支付网关只用新支持一种支付渠道,老的订单流程可以直接复用,计划通!!!!
实现
实现有不少参考了支付宝的文档,这里感谢一个
账号体系
账号体系这个有点偷懒了,在用户访问余额请求的时候,悄悄给开个户
通信校验
因为要和多个服务做通信,即使是在内网,也要有最基本的签名和验签功能,也就是支付宝自己生成一对密钥,私钥自己保存,公钥发放给对接的各个服务, 各个服务各自生成自己的密钥对,把公钥上传给支付宝用来做双方通信数据的校验。
签名
签名流程参考的是支付宝,简单讲就是先把表单里的关键字 key 去除(比如 sign, sign_type 这种),剩余的字段按照 key 的字典序排序,value 为 utf-8, 变成一个个的 kv 对,然后用 urlencode 变成一个字符串,对这个字符串配合私钥使用 rsa 的 SHA-1 签名出一个字符串,然后把结果串进行 b64 编码, 放在原表单的 sign 字段里
验签
和上面一样的逻辑得出本次 urlencode 的串,然后使用发送方的公钥以及表单里 b64Decode 的 sign 字段进行 rsa.verify
充值流程
这里主要是增加了一个预充值步骤,用户在选择完 IAP 商品后,先向服务器发送预充值请求,服务器根据用户和商品ID生成一个预充值请求ID返回给 APP, 然后APP再向苹果那边请求购买,购买成功的话苹果会向app通知一个4k大小的字符串,app把之前生成的预充值请求ID和这个串一起发送给服务器, 服务器来对对应账户的充值进行余额加减
消费流程
和支付宝流程一样,支付网关按照给定的算法对支付信息编码成串,向支付服务发起请求,支付服务解码信息串之后对相应的账户进行扣款, 扣款成功对支付信息里的回调地址进行回调。
这里注意为了省事儿是放在发起支付请求的一端把用户信息放入支付请求中,理论上应该是通过 app 唤醒/扫码支付这种流程, 请求方和支付方对于双方的账号体系是完全无感的。
退款流程
和消费流程基本一致,对余额进行增加,然后回调。
八大坑
看似苹果搞的这个东西还行,但是仔细琢磨下,再找找前人经验,坑的地儿可多了
- 首先需要每个人有一个 AppStore 账号:果子太想当然,我大清国情在此,人民群众并没有用邮箱的习惯,还得绑定银行卡,自动扣费这个事情接受度也不是很高
- 巨高的苹果税:15年9月份后,苹果要先扣除 2% 的交易税,然后按37分成,iOS 端平白少这么多钱,跟讲师们去沟通也是比较拙计
- 👆的苹果税不开发票:财务这边做账很困难
- 充值流程中苹果给app推送的字符串里的支付信息有类似 delay ack的逻辑,也就是如果前后脚购买了两个商品,最后会把这两个商品的购买信息合并放到一个字符串里
- 打款周期三个月,对账不好搞
- 打款的数额基本是对不上的
- 存在用户自己给苹果打电话退钱的事情
- 苹果给的账单报表基本没用