PHP接入海尔消费金融

170 阅读3分钟

海尔消费金融简称“嗨付”,公司要求接入海尔金融分期,第一次听说这种支付,官方只有java的demo,没有php的,所以下面会提供一些php实战用到的代码

文章目录

介绍

海尔会拉一个微信群,给一个word文档,上面会写着测试环境地址,很简洁,没有在线的接口文档,效率极低,我一共收到了3个word文档了,他们给的接口实例每次都要少点什么。开发完后,发现要注册他们平台的账号、身份证认证、人脸识别、银行卡添加,各种隐私,而且又是贷款,给人一种很不安全的感觉。作为后端,要测试自己写的代码,只能注册了。只要能写出来php的demo,官方给的文档在标准一些,接入就比较方便了,这两样还得自己摸索。。。

实战代码

线上和测试配置切换需要修改url(接口地址)

公共配置文件

公钥私钥文件夹

  • hai_pay/ 海尔金融配置文件夹
  • hai_pay/config/ 公钥私钥
  • hai_pay/config/private.pem 私钥
  • hai_pay/config/public.pem 公钥
  • hai_pay/RSAUtils.php
  • hai_pay/SignUtil.php

文件名:private.pem

-----BEGIN PRIVATE KEY-----
***
-----END PRIVATE KEY-----

文件名:public.pem

-----BEGIN PUBLIC KEY-----
***
-----END PUBLIC KEY-----

文件名:RSAUtils.php

<?php

namespace App\Http\Tools\HaiPay;

class RSAUtils
{
    private $privateKey='';//私钥(用于用户加密)
    private $publicKey='';//公钥(用于服务端数据解密)
    private $dir;

    public function __construct(){
        $this->dir = 'config';
    
    	$this->privateKey = openssl_pkey_get_private(file_get_contents(dirname(__FILE__).'/'.$this->dir.'/private.pem'));//私钥,用于加密
    	$this->publicKey = openssl_pkey_get_public(file_get_contents(dirname(__FILE__).'/'.$this->dir.'/public.pem'));//公钥,用于解密
    }
    
    /**
     * 私钥加密
     * @param 原始数据 $data
     * @return 密文结果 string
     */
    public function encryptByPrivateKey($data) {
    	openssl_private_encrypt($data,$encrypted,$this->privateKey,OPENSSL_PKCS1_PADDING);//私钥加密
    	$encrypted = base64_encode($encrypted);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
    	return $encrypted;
    }
    
    /**
     * 私钥解密
     * @param 密文数据 $data
     * @return 原文数据结果 string
     */
    public function decryptByPrivateKey($data){
    	$data = base64_decode($data);
    	openssl_private_decrypt($data,$encrypted,$this->privateKey,OPENSSL_PKCS1_PADDING);//私钥解密
    	return $encrypted;
    }
    
    /**
     * 私钥签名
     * @param unknown $data
     */
    public function signByPrivateKey($data){
    	openssl_sign($data, $signature, $this->privateKey);
    	$encrypted = base64_encode($signature);//加密后的内容通常含有特殊字符,需要编码转换下,在网络间通过url传输时要注意base64编码是否是url安全的
    	return $encrypted;
    }
    
    
    /**
     * 公钥加密
     * @param 原文数据 $data
     * @return 加密结果 string
     */
    public function encryptByPublicKey($data) {
    	openssl_public_encrypt($data,$decrypted,$this->publicKey,OPENSSL_PKCS1_PADDING);//公钥加密
    	return base64_encode($decrypted);
    }
    
    /**
     * 公钥解密
     * @param 密文数据 $data
     * @return 原文结果 string
     */
    public function decryptByPublicKey($data) {
    	$data = base64_decode($data);
    	openssl_public_decrypt($data,$decrypted,$this->publicKey,OPENSSL_PKCS1_PADDING);//公钥解密
    	return $decrypted;
    }
    
    /**
     * 公钥验签
     * @param unknown $data
     * @param unknown $sign
     */
    public function verifyByPublicKey($data,$sign){
    	$sign = base64_decode($sign);
    	return openssl_verify($data, $sign, $this->publicKey);
    }
    
    public function __destruct(){
    	openssl_free_key($this->privateKey);
    	openssl_free_key($this->publicKey);
    }
    
    public function getPrivateKey(){
    
        return file_get_contents(dirname(__FILE__).'/'.$this->dir.'/private.pem');
    }
}

文件名:SignUtil.php

<?php

namespace App\Http\Tools\HaiPay;

class SignUtil
{

    private $key = ''; /*密钥,22个字符*/
    private $iv  = ''; /*向量,8个或10个字符*/

    public function __construct($key = '',$iv = '')
    {
        $this->key = $key;
        $this->iv = $iv;
    }


    /**
     * 加密
     * @param boolean $status 是否加密
     * @return string 处理过的数据
     */
    public function encrypt($data,$status=false){
        if ($status){
            return base64_encode(openssl_encrypt($data, 'des-ede3-cbc', $this->key, OPENSSL_RAW_DATA,$this->iv));
        }
       return $data;
    }
 
    /**
     * 解密
     * @return string 加密的字符串不是完整的会返回空字符串值
     */
    public function decrypt($data,$miv,$status=false){
        if ($status){
            $k=substr($miv,0,8);
            $i=substr($miv,8,8);
            return openssl_decrypt(base64_decode($data), 'des-ede3-cbc', $k, OPENSSL_RAW_DATA, $i);
        }
        return $data;
    }
}

?>

控制器文件

<?php
namespace App\Http\Controllers\Pay;


use App\Exceptions\LogicException;
use App\Http\Model\DB\AwsOrders;
use App\Http\Tools\HaiPay\RSAUtils;
use App\Http\Tools\HaiPay\SignUtil;
use App\Http\Tools\Tools;
use Illuminate\Http\Request;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;

class HaiPayController extends BasicController
{
    protected $host;
    protected $loanType;
    protected $tools;
    protected $http_clinet;

    public function __construct(Request $request)
    {
        /**
         * applyNo是当前请求的一个请求号,方便查日志用的,没有其他作用。不与订单信息绑定。
         * uuid 每一笔订单都不要重复
         */
        parent::__construct($request);
        $this->host = 'https://testpm.haiercash.com:9002';
        $this->loanType = 0;
        if(env('APP_ENV') == 'production'){
            $this->loanType = 0; // todo 线上地址待完善
            $this->host = ''; // todo 线上地址待完善
        }
        $this->tools = new Tools();
        $this->http_clinet = new Client();
        require_once  dirname(dirname(dirname(__FILE__))) . '/Tools/hai_pay/SignUtil.php';
        require_once  dirname(dirname(dirname(__FILE__))) . '/Tools/hai_pay/RSAUtils.php';

    }

    /**
     * 贷款申请
     * @return \Illuminate\Http\JsonResponse
     * @throws LogicException
     * @throws \GuzzleHttp\Exception\GuzzleException
     * User: https://github.com/WXiangQian
     */
    public function hai_consume()
    {
        $key = rand(10000000, 99999999);
        $iv  = rand(10000000, 99999999);
        $order_id = $this->request->input('order_id',0);
        $model = $this->request->input('model','');
        // 期数。3、6、9、12
        $applyTnr = $this->request->input('applyTnr',0);
        if (!in_array($applyTnr,[3,6,9,12])) {
            return $this->response->tag('PARAM_ERROR')->response();
        }
        // 根据订单号查询数据库的实付金额
        $payAmt = 0;
        if (!isset($payAmt)) {
            return $this->response->tag('ORDER_NOT_EXIST')->response();
        }
        // 小于1w的课程不允许使用海尔金融
        if ($payAmt < 10000) {
            return $this->response->tag('PARAM_ERROR')->response();
        }

        $post_data = array(
            'uuid' => '交易流水号',
            'body' => array(
                'orderSn' => '订单号',
                'loanType' => '贷款品种编码',
                'payAmt' => '贷款金额',
                'orderDate' => '下单时间Y-m-d',
                'applyTnr' => '期数',
                'orderMessage' => array(
                    'cOrderSn' => time(),
                    'topLevel' => '',
                    'model' => '',
                    'sku' => 0,
                    'price' => '价格',
                    'num' => 1,
                    'cOrderAmt' => '价格',
                    'cOrderPayAmt' => '价格'
                ),
            )
        );

        $sign = new SignUtil($key, $iv);
        $desData = $sign->encrypt(json_encode($post_data), true);

        $rsa = new RSAUtils();
        $password_ = $rsa->encryptByPublicKey($key . $iv);

        $data = [
            'applyNo' => $order_id,
            'channelNo' => '',    // todo 文档里的渠道编号
            'tradeCode' => '', // todo 内部系统使用,文档里有标识
            'data' => $desData,
            'privatekey' => $rsa->getPrivateKey(),
            'key' => $password_
        ];
        $data_string = json_encode($data);
        $clinet_data = [
            'body' => $data_string,
        ];
        // 这里使用到了guzzle扩展包,可以更换为curl去请求
        $result = $this->http_clinet->request('post', $this->host.'/api/payment/gmorder/loanApplication', $clinet_data);

        if ($result->getStatusCode() != 200) {
            return $this->response->tag('PARAM_ERROR')->response();
        }
        $result = json_decode($result->getBody(),true);
        // 00000为申请成功
        if ($result['head']['retFlag'] != '00000') {
            throw new LogicException($result['head']['retMsg'],400);
        }

        $resData = $result['body']['data'];
        $resKey = $result['body']['key'];
        $decryptByPublicKey = $rsa->decryptByPrivateKey($resKey);

        $r = $sign->decrypt($resData, $decryptByPublicKey, true);
        $url = '';
        if ($r) {
            $r = json_decode($r, true);
            if (!empty($r['url'])) {
                $url = $r['url'];
            }
        }
        return $this->response->data(['url'=>$url])->response();
    }

    /**
     * 贷款回调
     * User: https://github.com/WXiangQian
     */
    public function hai_callback()
    {
        $post = file_get_contents('php://input');
        $rs = json_decode(stripslashes($post),true);
        if(empty($rs['key']) || empty($rs['data'])){
            echo json_encode(array('status'=>1,'msg'=>'key值或data值为空'),JSON_UNESCAPED_UNICODE); die;
        }
        $sign = new SignUtil();
        $rsa  = new RSAUtils();
        $decryptByPublicKey=$rsa->decryptByPrivateKey($rs['key']);
        $r = $sign->decrypt($rs['data'],$decryptByPublicKey,true);
        if(!$r){
            echo json_encode(array('status'=>1,'msg'=>'解密失败'),JSON_UNESCAPED_UNICODE); die;
        }
        $r = json_decode($r,true);
        if(!$r['ordersn']){
            echo json_encode(array('status'=>1,'msg'=>'没有订单编号'),JSON_UNESCAPED_UNICODE); die;
        }
        $out_trade_no   = $r['ordersn']; //订单号
        $transStatus    = $r['body']['outSts']; //订单状态 01审批中02审批通过03审批拒绝04 贷款已取消05 客人以确认提交(订单保存)06 审批退回(客人
        $trade_no       = $r['body']['orderNo']; //流水号
       // $notify_time    = time();       //通知的发送时间。格式为yyyy-MM-dd HH:mm:ss。

        if($transStatus == '02') {
            // 调用支付回调回调地址
           
           
            echo json_encode(array('status'=>1,'msg'=>'02处理成功'),JSON_UNESCAPED_UNICODE); die;
        }
        echo json_encode(array('status'=>1,'msg'=>'处理成功'),JSON_UNESCAPED_UNICODE); die;

    }
}

实战demo地址

github.com/WXiangQian/…

操作流程

结束语

真枪实战php接入海尔消费金融,希望可以得到一个👍哦