Android by external call micro-channel pay H5 browser, Android + PHP Comments

I read a lot of articles on micro letter to explain the development of H5 payments, mostly to call the payment interface (actually the number of public pay) by micro-channel internal browser, probably because H5 payment interface just open it soon.
Experience the official micro-channel link: http: //wxpay.wxutil.com/mch/pay/h5.v2.php, please open the micro-channel outside the browser.
Read the above link experience, if interested, you can read on, I hope for your help.

A, Android end

Android-side code is relatively simple, here I directly call system browser to open the payment page H5

 Intent intent = new Intent();
 intent.setAction("android.intent.action.VIEW");
 Uri content_url = Uri.parse(url); //url里面包含了后端需要用到的参数,例如金额,用户ID
 intent.setData(content_url);
 startActivity(intent);

At first considered using webview to load the payment page, but when debugging interface found has been reported the following error:

Micro-channel official example given in FIG.
The examples given in the official micro-channel can be seen, there should not be provided in webview referer caused. So, I follow the instructions to add the referer, however goose, and with no eggs, still error. I had to give up the use webview use the system browser. The shortcomings of the system browser was quite obvious, the client and pass value back hard to deal with (the article you are looking at, if the call is successful with webview, please enlighten me)

Two, PHP end

1. The acquisition parameters passed from the end over Android

Now I only think of a way is through carrying url parameter to the server by value, OK, get as follows:

//当前页面的URL地址为:http://www.XXXXX.com/wechatpay/h5Pay.php?money=100&uid=337828932
$string = $_SERVER['QUERY_STRING'];//通过$_SERVER['QUERY_STRING']函数获取url地址?后面的值
$androidData = convertUrlQuery($string);//将字符串$string转化为数组
$money = $androidData['money '];//获取money值 100
$uid = $androidData['uid'];//获取用户ID 337828932

the function code used above convertUrlQuery

//将字符串参数变为数组
function convertUrlQuery($query)
{
    $queryParts = explode('&', $query);
    $params = array();
    foreach ($queryParts as $param) {
        $item = explode('=', $param);
        $params[$item[0]] = $item[1];
    }
    return $params;
}
2. Call the micro-channel unified under a single API
2.1 Specific Parameter name and purpose of each parameter, please see the micro-channel official document unified under a single API
    //配置需要传递给微信的参数
    $input = new WxPayUnifiedOrder();
    $input->SetBody("xxxx-商品购买");
    $input->SetAttach("xxxx");
    $input->SetDevice_info("WEB");
    $input->SetOut_trade_no(WxPayConfig::MCHID . date("YmdHis").$uid);//把UID加在末尾主要用于识别用户,给对应的用户充值余额
    $input->SetTotal_fee($money);
    $input->SetTime_start(date("YmdHis"));
    $input->SetTime_expire(date("YmdHis", time() + 600));
    $input->SetGoods_tag("t");
    $input->SetNotify_url("http://www.xxxxx.com/wechatpay/notify.php");//用于接收微信下发的支付结果通知
    $input->SetTrade_type("MWEB");
    $input->SetOpenid($openId);
    $input->SetScene_info("{\"h5_info\": \"h5_info\"{\"type\": \"Wap\",\"wap_url\": \"http://www.xxxxx.com/shop\",\"wap_name\": \"xxx\"}}");
Followed by 2.2 $order = WxPayApi::unifiedOrder($input);acquisition parameters returned by the micro-channel, unifiedOrder realize a specific function
/**
     * 
     * 统一下单,WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param WxPayUnifiedOrder $inputObj
     * @param int $timeOut
     * @throws WxPayException
     * @return 成功时返回,其他抛异常
     */
    public static function unifiedOrder($inputObj, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        //检测必填参数
        if(!$inputObj->IsOut_trade_noSet()) {
            throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
        }else if(!$inputObj->IsBodySet()){
            throw new WxPayException("缺少统一支付接口必填参数body!");
        }else if(!$inputObj->IsTotal_feeSet()) {
            throw new WxPayException("缺少统一支付接口必填参数total_fee!");
        }else if(!$inputObj->IsTrade_typeSet()) {
            throw new WxPayException("缺少统一支付接口必填参数trade_type!");
        }
        
        
        //异步通知url未设置,则使用配置文件中的url
        if(!$inputObj->IsNotify_urlSet()){
            $inputObj->SetNotify_url(WxPayConfig::NOTIFY_URL);//异步通知url
        }
        
        $inputObj->SetAppid(WxPayConfig::APPID);//公众账号ID
        $inputObj->SetMch_id(WxPayConfig::MCHID);//商户号
        $inputObj->SetSpbill_create_ip(self::getIp());//终端ip
        //$inputObj->SetSpbill_create_ip("1.1.1.1");        
        $inputObj->SetNonce_str(self::getNonceStr());//随机字符串
        
        //签名
        $inputObj->SetSign();
        $xml = $inputObj->ToXml();
        
        $startTimeStamp = self::getMillisecond();//请求开始时间
        $response = self::postXmlCurl($xml, $url, false, $timeOut);
        $result = WxPayResults::Init($response);
        self::reportCostTime($url, $startTimeStamp, $result);//上报请求花费时间
        
        return $result;
    }
Note that the above code 2.3 has the following three points
  • Get real IP address of the customer service side of

    Use REMOTE_ADDR IP the visitor can only get the local connection settings. After I rough test, when using the UC browser will not get the real IP directly, if it is to pass over this IP, micro-channel will return to the network environment through secure authentication failed, please try again later Error

Then you can get the user's real IP using the following methods

//获取用户真实IP
    public static function getIp(){
        $ip = '';
        if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        }elseif(isset($_SERVER['HTTP_CLIENT_IP'])){
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        }else{
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        $ip_arr = explode(',', $ip);
        return $ip_arr[0];
    }
  • The conversion parameters into a XML format

    In this era of rampant JSON, also pass data in XML format, and perhaps this is the high-end micro-channel operation, perhaps their previous framework is the way, which we would not care. Directly on the code

/**
     * 输出xml字符
     * @throws WxPayException
    **/
    public function ToXml()
    {
        if(!is_array($this->values) 
            || count($this->values) <= 0)
        {
            throw new WxPayException("数组数据异常!");
        }
        
        $xml = "<xml>";
        foreach ($this->values as $key=>$val)
        {
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml; 
    }
  • Signature method

Signature generation general procedure is as follows:

The first step, provided to send or receive all the data set M, the argument is not null values ​​within the set of parameter name of the parameter M according to the ASCII code in ascending order (lexicographic), using the format of the URL of the key (i.e., key1 = value1 & key2 = value2 ...) assembled into a string stringA.

Particular attention to the following important rules:

◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段

The second step, in the final stitching key stringA get stringSignTemp string, and stringSignTemp MD5 operation performed, and then the resulting string of all characters to uppercase, get sign value signValue.

key set the path: micro-channel merchant platform (pay.weixin.qq.com) -> Account Settings -> the API Security -> Key Set

/**
     * 生成签名
     * @return 签名
     */
    public function MakeSign()
    {
        //签名步骤一:按字典序排序参数
        ksort($this->values);
        $string = $this->ToUrlParams();
        //签名步骤二:在string后加入KEY
        $string = $string . "&key=".WxPayConfig::KEY;
        //签名步骤三:MD5加密
        $string = md5($string);
        //签名步骤四:所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
    /**
     * 格式化参数  --格式化成url参数
     */
    public function ToUrlParams()
    {
        $buff = "";
        foreach ($this->values as $k => $v)
        {
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
        
        $buff = trim($buff, "&");
        return $buff;
    }
2.4 mweb_url parameters returned by invoking micro-channel APP
if ($order['mweb_url']) {
        $orderId = $input->GetOut_trade_no();
        $payUrl = $order['mweb_url']."&redirect_url=http%3A%2F%2Fwww.xxxx.com%2Fwechatpay%2Forderquery.php%3Ftransaction_id%3D".$orderId;
        Log::DEBUG($payUrl);
        //通过JS打开链接,调起微信APP支付
        echo "<script LANGUAGE='Javascript'>";
        echo "location.replace('$payUrl')";
        echo "</script>";
    }

A detailed explanation about the meaning of this parameter payUrl in redirect_url

Will return to the normal flow of users pay to complete to initiate the payment page, you want to return to a specified page, the parameters can be spliced on redirect_url after MWEB_URL, to specify a callback page.
For example, you want the user to pay after the completion of the jump to https://www.wechatpay.com.cn, can be handled as follows:
Suppose you won through to the unified under a single interface to MWEB_URL = https://wx.tenpay.com / cgi-bin / mmpayweb-bin / checkmweb? prepay_id = wx20161110163838f231619da20804912345 & package = 1037687096
address is spliced to MWEB_URL = https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096&redirect_url= https% 3A% 2F% 2Fwww.wechatpay.com.cn
Note:
1. urlencode need to be processed redirect_url
2. Since the setting redirect_url, rebound specified page operation may occur: 1, micro-channel middle page payment cash register micro-channel invoking Taiwan after more than five seconds 2, the user clicks "cancel payment" or the payment is complete click "Finish" button. There is no guarantee when the page rebound, the payment process has ended, so redirect_url address of the business setting can not be performed automatically check a single operation, should allow users to click a single button to trigger the search operation, and stitching me back $ orderId is used to check a single operation parameters. Bounce page showing the effect can refer to the figure

Callback page .png example of FIG.

3. The micro-channel payment processing results notification

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。
对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)
注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。

支付回调基础类

/**
 * 
 * 回调基础类
 * @author widyhu
 *
 */
class WxPayNotify extends WxPayNotifyReply
{
    /**
     * 
     * 回调入口
     * @param bool $needSign  是否需要签名输出
     */
    final public function Handle($needSign = true)
    {
        $msg = "OK";
        //当返回false的时候,表示notify中调用NotifyCallBack回调失败获取签名校验失败,此时直接回复失败
        $result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
        if($result == false){
            $this->SetReturn_code("FAIL");
            $this->SetReturn_msg($msg);
            $this->ReplyNotify(false);
            return;
        } else {
            //该分支在成功回调到NotifyCallBack方法,处理完成之后流程
            $this->SetReturn_code("SUCCESS");
            $this->SetReturn_msg("OK");
        }
        $this->ReplyNotify($needSign);
    }
    
    /**
     * 
     * 回调方法入口,子类可重写该方法
     * 注意:
     * 1、微信回调超时时间为2s,建议用户使用异步处理流程,确认成功之后立刻回复微信服务器
     * 2、微信服务器在调用失败或者接到回包为非确认包的时候,会发起重试,需确保你的回调是可以重入
     * @param array $data 回调解释出的参数
     * @param string $msg 如果回调处理失败,可以将错误信息输出到该方法
     * @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
     */
    public function NotifyProcess($data, &$msg)
    {
        //TODO 用户基础该类之后需要重写该方法,成功的时候返回true,失败返回false
        return true;
    }
    
    /**
     * 
     * notify回调方法,该方法中需要赋值需要输出的参数,不可重写
     * @param array $data
     * @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
     */
    final public function NotifyCallBack($data)
    {
        $msg = "OK";
        $result = $this->NotifyProcess($data, $msg);
        
        if($result == true){
            $this->SetReturn_code("SUCCESS");
            $this->SetReturn_msg("OK");
        } else {
            $this->SetReturn_code("FAIL");
            $this->SetReturn_msg($msg);
        }
        return $result;
    }
    
    /**
     * 
     * 回复通知
     * @param bool $needSign 是否需要签名输出
     */
    final private function ReplyNotify($needSign = true)
    {
        //如果需要签名
        if($needSign == true && 
            $this->GetReturn_code($return_code) == "SUCCESS")
        {
            $this->SetSign();
        }
        WxpayApi::replyNotify($this->ToXml());
    }
}

需要注意的是,如果用户只是打开付款界面,而没有执行付款操作的话,是不会触发通知的。

当我们接收到的参数中同时含有return_code,result_code,trade_state并且每个返回值都为SUCCESS的时候,代表用户付款成功

if(array_key_exists("return_code", $result)
        && array_key_exists("result_code", $result)
    &&array_key_exists("trade_state", $resultData)
        && $resultData["trade_state"] == "SUCCESS"
        && $result["return_code"] == "SUCCESS"
       && $result["result_code"] == "SUCCESS")
        {
            //这里执行给用户充值余额的操作
        }    
4.redirect_url回调页面处理

如果在第2.4步骤中MWEB_URL后拼接上了redirect_url参数,用户支付完成后将返回到这个页面。
当用户点击界面的已完成按钮,将触发查单操作
微信查单操作API
查单操作相关代码如下

//查询订单
    public function Queryorder($transaction_id)
    {
        $input = new WxPayOrderQuery();
        $input->SetTransaction_id($transaction_id);
        $result = WxPayApi::orderQuery($input);
        Log::DEBUG("query:" . json_encode($result));
        if(array_key_exists("return_code", $result)
            && array_key_exists("result_code", $result)
            && $result["return_code"] == "SUCCESS"
            && $result["result_code"] == "SUCCESS")
        {
            return $result;
        }
        return false;
    }

界面布局以及相关的逻辑处理:

if(isset($_REQUEST["out_trade_no"]) && $_REQUEST["out_trade_no"] != ""){
    $out_trade_no = $_REQUEST["out_trade_no"];
    $input = new WxPayOrderQuery();
    $input->SetOut_trade_no($out_trade_no);
    Log::DEBUG("out_trade_no".$out_trade_no);
//  printf_info(WxPayApi::orderQuery($input));
    $result=WxPayApi::orderQuery($input);
    if(array_key_exists("return_code", $result)
        && array_key_exists("result_code", $result)
        && array_key_exists("trade_state", $result)
        && $result["trade_state"] == "SUCCESS"
        && $result["return_code"] == "SUCCESS"
        && $result["result_code"] == "SUCCESS"){
        $successUrl="success.html";
        echo "<script LANGUAGE='Javascript'>";
        echo "location.replace('$successUrl')";
        echo "</script>";
    }else{
        $failUrl="fail.html";
        echo "<script LANGUAGE='Javascript'>";
        echo "location.replace('$failUrl')";
        echo "</script>";
    }
    exit();
}
?>
<script type="text/javascript">
    ///
    window.onload=function () {
        var Request=new UrlSearch(); //实例化
        document.getElementById("out_trade_no").value=Request.transaction_id;
        document.getElementById("transaction_id").style.height = 0;
        document.getElementById("out_trade_no").style.height = 0;
        document.getElementById("btn").style.height = 0;
        $.DialogByZ.Confirm({Title: "", Content: "请确认微信支付是否已完成",FunL:confirmL,FunR:Immediate})
    }
    ///
    function confirmL(){
        $.DialogByZ.Close();
        document.getElementById('btn').click();
    }
    function Immediate(){
        //location.href="http://sc.chinaz.com/jiaoben/"
        window.location.href="nonglegou://data/?state=fail";
    }
    function UrlSearch()
    {
        var name,value;
        var str=location.href; //取得整个地址栏
        var num=str.indexOf("?")
        str=str.substr(num+1); //取得所有参数   stringvar.substr(start [, length ]

        var arr=str.split("&"); //各个参数放到数组里
        for(var i=0;i < arr.length;i++){
            num=arr[i].indexOf("=");
            if(num>0){
                name=arr[i].substring(0,num);
                value=arr[i].substr(num+1);
                this[name]=value;
            }
        }
    }
</script>

三、总结

微信H5支付的整个流程,大概就是这样子,以下是流程图

Interface flow chart

  1. 用户在商户侧完成下单,使用微信支付进行支付
  2. Initiate single request (call unified under a single interface) by the merchant back to the micro-channel payment Note: Transaction type trade_type = MWEB
  3. Unified under a single interface to return back to the merchant payment-related parameters, such as payment jump url (parameter name "mweb_url"), a merchant by invoking mweb_url micro-channel payment page middle
  4. Check, security check intermediate pages H5 rights (see below for common mistakes here)
  5. Such as the successful payment, the merchant will receive asynchronous notifications background micro-channel side
  6. In the micro-channel users to complete payment of the cash register to pay or cancel payment and return the merchant page (the default is to return payment initiation page)
  7. In show business page guide the user initiates the result of payment of inquiry
  8. Business background to determine whether the results received a notice to pay income micro-channel side, and if not, call our order back to confirm the order status query interface
  9. The final results show orders for payment to the user

Please indicate the source: http: //www.jianshu.com/p/e49c8003c0e8
Author: TSY
personal space: https: //www.hxkj.vip

Like, you can look at my micro-channel public number

Micro-channel public number

Guess you like

Origin www.cnblogs.com/hashtang/p/11455773.html