Google Wallet 支付接入(内购买商品)后端处理逻辑及问题分析

Google Wallet 支付接入(内购买商品)后端处理逻辑及问题分析


前期准备

首先要创建一个Google play 开发者帐号,创建帐号需要绑定VISTA信用卡,细节这里不在多讲。

接下来创建Oauth2.0授权访问,选择web application.创建成功后我们拿到client_id和client_secret,接下来需要对访问者(谷歌帐号)进行授权。这里Google Pay与Google的帐号是分离的,但是可以关联到一起。开发者帐号可以通过发布邀请,关联其他开发者共同开发和维护平台。

如果上面的步骤你已经完成了那么继续,获取AccessToken, AccessToken是访问Google Pay 接口的一把开门的钥匙。那么如何获取呢,如果你仔细研读过官方Oauth2.0的文档就知道这并不是一件很困难的事儿。(附:https://developers.google.com/identity/protocols/OAuth2)。好吧,如果文档你看着头疼那么你可以考略去github上下载最新的相关语言的SDK,不过SDK中包含了太多我们不会用的业务逻辑,比较繁重。下面的方法可能会更直接一些。
Google Wallet 支付接入(内购买商品)后端处理逻辑及问题分析_第1张图片

Step 1.生成Code:
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri={your_web_index}&client_id={your_client_id}
访问成功后会弹出一个对话框,点击同意即可,如果你没有登录,请登录您被开发者邀请共同开发且已经授权的帐号,切记这里的帐号一定是在Google Pay Control中可以查到的并已经授于访问权限的。接下来浏览器会定位到你的Web Index页面。并在浏览器地址上多出了?code=……的字符串,取出这个code。
Step 2.获取Refresh Token:
开发过Google的同学可能比较清除,AccessToken是会在1个小时内过期的,但是RefreshToken是不会过期的。
POST https://accounts.google.com/o/oauth2/token
参数名 参数值
code {your_code}
client_id {your_client_id}
client_secret {your_client_secret}
redirect_uri {you_redirect_url}
grant_type authorization_code
请求成功后,返回了refresh_token,把它保存好下次在请求就不会有了,有了它我们就再也不怕AccessToken过期了。但是AccessToken还是会在一个小时内过期的这里可以通过下面方法重新获取
POST https://accounts.google.com/o/oauth2/token
参数名 参数值
client_id {your_client_id}
client_secret {your_client_secret}
refresh_token {your_refresh_token}
grant_type refresh_token
这里建议用redis来保存,并设置过期时间最好小于一个小时。请求的过程中最好锁定避免多线程或集群的机器同时修改了这个值导致请求后面的请求验证失败。附上PHP代码仅供参考

define(‘ACCESS_TOKEN’, ‘Google-Oauth-Access-Token’);
define(‘ACCESS_TOKEN_LOCK’, ‘Google-Oauth-Access-Token-lock’);

 /**

 * 获取AccessToken

 * @param null

 * @throws Exception

 * @return string|bool

 */

public function getAccessToken(){

    $body = [

        'grant_type'    => $this->_grant_type,

        'client_id'     => $this->_client_id,

        'client_secret' => $this->_client_secret,

        'refresh_token' => $this->_refresh_token

    ];

    $access_token = Global_Redis::getInstance()->get(ACCESS_TOKEN);

    if(!$access_token){

        try{

            do{

                /**

                 * 判断锁定循环

                 */
             if(Global_Redis::getInstance()->get(ACCESS_TOKEN_LOCK) == 1){

                    sleep(1);

                }else{

                    /**

                     * 锁定、最多5秒自动解锁

                     */

                    Global_Redis::getInstance()->set(ACCESS_TOKEN_LOCK, 1, 5);

                    $result = Global_Http::post($this->_oauth_url, $body);

                    /**

                     * 解除锁定

                     */

                Global_Redis::getInstance()->del(ACCESS_TOKEN_LOCK);
                    break;

                }

            }while(1);

        }catch(Exception $e){


        }

        if(isset($result['access_token']) && $result['access_token']) {

            $access_token = $result['access_token'];

            Global_Redis::getInstance()->set(ACCESS_TOKEN, $access_token, 3000);

        }else{

            throw new Pay_Service_GoogleWallet_Exception("Google Wallet OAuth Error: Get AccessToken Fail!");

        }

    }

    return $access_token;
}

OK,当我们拿到AccessToken前期的准备工作就算完成了。

支付流程设计

Google Pay 支持两种商品类型:1.标准应用内商品、2.订阅商品。这里重点介绍应用内商品,订阅商品请以此类推。

Google Pay对商品进行了购买托管。用户购买的商品必须消耗完成才能继续购买(是不是感觉有点坑)。

目前Google暴露给我们得接口可用的上的就是查询订单状态。也就是说订单的支付、消费、消耗都是由Android端通过应用内部实现的。不过接口虽然少但是我们也完全可以设计出一套完美的后端验证支付过程。
(附文档接口:https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/get)

开发时序图如下:
Google Wallet 支付接入(内购买商品)后端处理逻辑及问题分析_第2张图片

客户端通过配置好的商品ID向google请求商品列表(商品描述和价格),生成商品列表后,用户就可以点击商品吊起支付,具体的方法相见官方Andriod开发文档。

当支付完成后,Google Pay 会返回给Andriod端支付相关信息:

{
"orderId":"12999763169054705758.1371079406387615", "packageName":"com.example.app",
"productId":"exampleSku",
"purchaseTime":1345678900000,
"purchaseState":0, "developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
"purchaseToken":"rojeslcdyyiapnqcynkjyyjh"
}

在这里获得后端通过接口验证订单的3个重要参数,productId(商品ID),purchaseToken(支付token),packageName(产品包名)。

  • Andriod端把这三个参数提交给服务端(最好把google的orderID一块提交存储,便于以后补单和防止重复创建相同的订单,这里还有其他的附加信息例如:用户ID,汇率,渠道等等用于创建订单的信息),Pay Server服务接收参数后去Google验证订单的有效性(接口相见上面的内容)。当判断订单是已支付且未消耗状态时(purchaseState = 0, consumptionState = 0),创建自己的订单。如果接口服务器和支付服务器(Pay Server)是分离的,这个时候也要创建接口服务器的订单。创建订单后返回给Andriod端Pay Server的orderID。

  • Andriod端调用方法consumePurchase()请求Google Pay进行已购商品消耗(Google内置商品管理,如果不消耗无法继续购买同一个商品)。消耗成功后携带上面请求获得的Pay Server orderID 通知Pay Server已购商品消耗成功,Pay Server请求Google(和上面的同一个接口)判断已购商品的消耗状态(purchaseState = 0, consumptionState = 1),修改自己服务的订单状态为已完成。通知接口服务端(web端)充值到用户,完成充值。

Q&A

如果别人反编译了你的包修改内置的请求参数把自己的用户写进去,上传到google那么下载的用户充值都充进了反编译者的钱包?

解答:Google pay会对APP的签名进行认证包名也是唯一的,仿制无法吊起充值。

如果流程中出现手机断电,手机断网,Pay Server、Web Server宕机等问题怎么办?

解答:第一种情况,充值成功后,访问Pay Server创建订单失败,这个时候Andriod端可以在Google Pay获得到当前用户未消耗的所有订单,每次吊起支付都可以重新请求Pay Server继续执行。第二种情况,消耗成功后,访问PayServer充值失败。此时可人为通过查询Pay Server的为支付订单结合Google Pay后台进行对比证实补单。也可以写程序后台自动补单。依据是Google Pay订单已支付,Pay Server订单已创建未完成(Pay Server订单创建时要保存Google Pay的orderID)。

google的商品价格限定0.5一个阶段 也就是说只能是以.49或者.99结尾的价格存在。

你可能感兴趣的:(支付)