OPPO手机支付流程以及服务端php回调校验

OPPO手机支付流程如下:

1.前端拉起商品列表

用户登录app后,进入商品购买页面,前端请求app商品列表api接口,获取商品列表信息并展示

2.下单

用户点击'购买'操作,发送给服务端一条当前商品信息,服务端保存该商品的相关订单数据,并返回给前端oppo支付所需要的请求数据,前端调用oppo支付sdk,发起支付请求
    /**
     * oppo支付下单
     * @param array $params 订单信息
     * @return array
     */
    public static function Pay($params)
    {
        //获取商品内部订单号
        $innerOno = self::genInnerOno(date("Y-m-d H:i:s"), $params['user_id'], $paymentType);
        //生成订单
        $order = new Order();
        $order->order_id = $innerOno;  // 内部订单号
        $order->product_name = $params['name'];  // 商品名称
        $order->number = $params['num'];  // 购买商品个数
        $order->price = $params['price'];  // 商品单价, 1个商品价格
        $order->total_price = $params['total_price'];  // 总的价格
        $order->pay_amount = $params['pay_amount'];  // 应付总额:  实际应该支付的价格= 总金额 - 优惠金额
        $order->count = $params['count'];  // 购买后得到商品对应的产品数量
        $order->status = 0;  // 状态:0 待支付     
        $order->user_id = $params['user_id'];  // 用户游戏ID
        $order->expired_at = $params['expired_at'];  // 过期时间
        $order->created_by = $params['created_by'];  // 创建者
        $order->updated_by = $params['updated_by'];  // 创建者
 
        $result = $order->save();
        if (!$result) {
            $errors = '';
            if ($order->hasErrors()) {
                $tmp = $order->getErrors();
                foreach ($tmp as $rows) {
                    foreach ($rows as $row) {
                        $errors .= $row . '<br/>';
                    }
                }
            }
            throw new \Exception($errors);
        }
        $orderId = $order->id;
        
        //构建oppo支付所需要的数据
        $title = $order->product_name;
        //过滤掉特殊字符
        $title = replaceStr($title);
        $config = Yii::$app->params['oppo'];
        $price = $order->pay_amount;
 
         //构建向oppo发送的参数
        $data = [
            'order' => $model->order_id, //商户订单号,务必保证唯一
            'amount' => $price,  //消费总金额,单位为分
            'productName' => $title,  //商品名(不能含有+号等特殊符号
            'productDesc' => $title,  //商品描述(不能含有+号等特殊符号)
            'callbackUrl' => $config['notify'],  //回调地址
        ];

        return jsonFail($data);
    }
 
   /**
     * 生成本系统内部订单号, 在向第三方服务商发起支付时需要使用.
     *
     * 下单时的处理流程:
     *   1. 属性 ono 不要赋值, 成功插入(保存)订单.
     *   1. 支付前生成本系统内部订单号, 向第三方发起支付请求.
     *   1. 接收到支付成功回调通知时, 将第三方订单号保存到本次付款的订单的 ono 字段上.
     *
     * 格式: 下单时间+支付方式+ 4位数字修正值. 如 "20150930140041+1+1234".
     * 最大长度 32 个字符. 这也是 wx 可接受的订单号最大长度.
     *
     * @param int $createdAt 订单创建时间戳, 即 下单时间戳.
     * @param int $createdBy 订单创建人 ID, 即 下单会员 ID.
     * @return string
     * @throws \Exception
     */
 
    public static function genInnerOno($createdAt, $createdBy)
    {
        $createdAt = strtotime($createdAt);
        if (1 > $createdAt || 1 > $createdBy) {
            throw new \Exception('参数错误');
        }
 
        // 生成一个修正值, 一定程度上增加订单号的随机性.
        $tmp = intval(substr($createdAt, -4)) + intval(substr($createdBy, -2));
        if (4 < strlen($tmp)) {
            $tmp = substr($tmp, -4);
        }
 
        $tmp = date('YmdHis', $createdAt) . $tmp . rand(100, 900);
        return $tmp;
    }

3. 支付

前端获取服务端返回的支付所需要的请求数据后,拼接支付请求参数,调用oppo支付sdk,发起支付请求

4. 回调操作

前端支付成功后,oppo SDK 服务器会根据开发上传的回调地址请求服务端的回调方法(回调方法为 HTTP POST。 Content-type:application/x-www-form-urlencoded),对支付订单进行校验验签操作,根据验签结果,处理订单业务逻辑
php以yii2框架为参考
    /**
     * oppo支付回调
     */
    public function actionNotify()
    {
        //获取回调通知数据
        $inBodyArray = $_POST;  

        //判断通知请求
        $trans = Yii::$app->db->beginTransaction();
        try {
            //验证签名
            //参数名         类型     长度限制   说明
            //notifyId     string     50      回调通知 ID(该值使用系统为这次支付生成的订单号)
            //partnerOrder string     100     开发者订单号(客户端上传)
            //productName  string     40      商品名称(客户端上传)
            //productDesc  string     120     商品描述(客户端上传)
            //price         int               商品价格(以分为单位)
            //count         int               商品数量(一般为 1)
            //attach       string             请求支付时上传的附加参数(客户端上传)
            //sign         string             签名
            $params['notifyId'] = $inBodyArray["notifyId"];
            $params['partnerOrder'] = $inBodyArray["partnerOrder"];
            $params['productName'] = $inBodyArray["productName"];
            $params['productDesc'] = $inBodyArray["productDesc"];
            $params['price'] = $inBodyArray["price"];
            $params['count'] = $inBodyArray["count"];
            $params['attach'] = $inBodyArray["attach"];
            $params['sign'] = $inBodyArray["sign"]; //签名
            //校验签名
            OppoPay::signCheck($params);
            // 处理业务逻辑
            $order_number = $inBodyArray["notifyId"];    //回调通知 ID(该值使用系统为这次支付生成的订单号)
            $order_id = $inBodyArray['partnerOrder']; //商户订单号(商户内部生成的订单号)
            // 订单状态
            $status = UserOrder::STATUS_BUY;
            
            //处理订单业务逻辑
            //判断订单状态是否已经支付
            $userOrder = UserOrder::find()
                        ->where([
                            'status' => $status,
                            'order_id' => $order_id
                         ])
                        ->exists();
            if ($userOrder) {
                throw new \Exception('订单已支付');
            }
            // 变更订单属性
            UserOrder::changeAttribute(
                ['order_id' => $order_id],
                [
                    'status' => $status,
                    'paid_at' =>  time(), //支付时间戳
                    'order_number' => $order_number,
                ]
            );

            $trans->commit();

            $result = [
                'result' => "OK",
                'resultMsg' => '',
            ];
        } catch (\Exception $e) {
            $result = [
                'result' => "FAIL",
                'resultMsg' => $e->getMessage(),
            ];
            $trans->rollBack();
        }
     echo json_encode($result);
    }

5.上面需要使用到的公共方法

/**
 * 操作成功
 * @example1 jsonSuccess();
 */
function jsonSuccess($data = NULL, $message = '操作成功')
{
    header("Content-Type:application/json");
    $json = array();
    $json['code'] = 0;
    $json['data'] = $data;
    $json['message'] = $message;
    $json['request_time'] = now();
    echo Json::encode($json);
    exit();
}

/**
 * 错误信息
 * @example1 jsonFail();
 * @example1 jsonFail("删除失败!");
 * @example2 jsonFail($model->getErrors());
 */
function jsonFail($message = '请求失败', $code = 1, $data = null)
{
    header("Content-Type:application/json");
    $json = array();
    $json['code'] = $code;
    $json['data'] = $data;
    $json['message'] = $message;
    $json['request_time'] = now();
    echo Json::encode($json);
    exit();
}

/**
 * 过滤掉特殊的字符
 * @param $str
 * @param string $replacement
 * @return string|string[]|null
 */
function replaceStr($str, $replacement = '')
{
    $regex = "/\/|\~|\,|\。|\!|\?|\“|\”|\【|\】|\『|\』|\:|\;|\《|\》|\’|\‘|\ |\·|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\+|\{|\}|\:|\<|\>|\?|\[|\]|\,|\.|\/|\;|\'|\`|\-|\=|\\\|\|/";
    $result = preg_replace($regex, $replacement, $str);

    return $result;
}
Yii2 OPPO相关配置:params-local.php
<?php

return [
    //oppo配置
    'oppo' => [
        'app_id' => 'xxx',  //app id
        'app_key' => 'xxx', //app_key
        'app_secret' => 'xxx',//app_secret
        //oppo异步通知地址
        'notify' => "http://xxx/notify",
        'pay_test' => 1,  //是否支付测试
        'is_screen_pay' => 1, //是否屏蔽支付
    ],
];
OPPO支付签名校验
<?php
/**
 * oppo支付签名校验
 */
namespace oppo;

/**
 * Class Pay
 * @package oppo
 */
class Pay
{
    /**
     * @param $params
     * @throws \Exception
     */
    public static function signCheck($params)
    {
        $result = self::rsa_verify($params);
        if ($result != 1) { //验证失败
            throw new  \Exception($result);
        }
    }

    /**
     * 校验签名
     * @param $contents
     * @return int
     */
    public static function rsa_verify($contents)
    {
        $str_contents = "notifyId={$contents['notifyId']}&partnerOrder={$contents['partnerOrder']}&productName={$contents['productName']}&productDesc={$contents['productDesc']}&price={$contents['price']}&count={$contents['count']}&attach={$contents['attach']}";
        $publickey = ''; //公钥
        $pem = chunk_split($publickey, 64, "\n");
        $pem = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----\n";
        $public_key_id = openssl_pkey_get_public($pem);
        $signature = base64_decode($contents['sign']);
        return openssl_verify($str_contents, $signature, $public_key_id);//成功返回1,0失败,-1错误,其他看手册
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值