微信支付方式区分

摘要:
chapter=7_1使用场景:JSAPI支付是指商家在支付场景中调用微信支付提供的JSAPI接口,从微信支付模块完成收款。使用示例A:离线示例B:官方帐户场景C:PC网站场景A:B:C:JSAPI支付指令$res){DB::rollBack();thrownewLogicException;}DB::commit();回声“成功”;死亡;}捕获{writeLogs;//写入日志记录DB::rollBack();echo“fail”;die;}}呼应“成功”;die;}}/***生成签名-重写方法*@paramarray$data配置对象**@returnfloat*/publicfunctionquery_weixin_Sign{$wxconfig=config;//签名步骤1:按字典顺序对参数ksort排序;$checkSign=$data['Sign'];unset;$string=$this-˃to_url_params;//签名第2步:添加KEY$string=$string.“&KEY=”.$wxcnfig['KEY'];//签名步骤3:MD5加密或HMAC-SHA256if{//如果签名数小于或等于32,请使用MD5验证$string=MD5;}否则{//使用sha256验证$string=hash_hmac;}//签名步骤4:所有字符都大写$result=strtoupper;写日志;//如果{returntrue;},则写入日志记录否则{returnfalse;}}公共函数from_xml{if(!

第一: JSAPI支付     https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

使用场景:JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款

使用实例 A:线下实例   B:公众号场景   C:PC网站场景

           A:微信支付方式区分第1张                              B:     微信支付方式区分第2张 C:                        微信支付方式区分第3张

 JSAPI支付使用说明  

<?php
namespace AppHttpControllersApi;

use AppApiHelpersMoveNumber;
use AppHttpControllersController;
use AppHttpMiddlewareApiLog;
use AppModelsMShopOrders;
use IlluminateHttpRequest;
use IlluminateSupportFacadesDB;
use IlluminateSupportFacadesValidator;
use EasyWeChatFactory;

/**
 * 订单
 *
 */
class NotifyController extends Controller {
    use MoveNumber;
    /**
     * 支付回调
     *
     */
    public function notify_wx()
    {
        writeLogs([], '已进入到微信回调', 'notify'); // 写入日志记录
        $notify_data = file_get_contents("php://input");
        $notify_data = $this->from_xml($notify_data);
        writeLogs($notify_data, '回调返回数据', 'notify'); // 写入日志记录
        if (!$notify_data) {
            writeLogs($notify_data, '未接收到信息', 'notify'); // 写入日志记录
            $notify_data = $GLOBALS['HTTP_RAW_POST_DATA'] ?: '';
        }
        // 验签
        $return_data = $this->query_weixin_sign($notify_data);
        if (!$return_data) {
            writeLogs($notify_data, '验签失败', 'notify'); // 写入日志记录
            echo 'fail';
            die;
        }
        writeLogs($notify_data, '验签通过', 'notify'); // 写入日志记录

        if ($notify_data['return_code'] == 'SUCCESS' && $notify_data['result_code'] == 'SUCCESS') {
            $order_id = $notify_data['out_trade_no'];
            $check_data = MShopOrders::where(['order_sn' => $order_id])->first();
            if ($check_data['status'] == 0) {
                // 事务开启
                DB::beginTransaction();
                try {
                    // 更改订单状态
                    $result = MShopOrders::where(['order_sn' => $order_id])->update(['status'=> 1]);
                    if ( !$result ) {
                        DB::rollBack();
                    }
                    // 创建订单  预占号
                    $move_order_id = $this->make_phone_order($order_id);
                    if ( $move_order_id == -1 ) {
                        DB::rollBack();
                        throw new LogicException('创建预占号订单失败', 201);
                    }
                    // 预占号成功  更新订单数据
                    $res = MShopOrders::where(['order_sn' => $order_id])->update(['order_operator'=> $move_order_id]);
                    if ( !$res ) {
                        DB::rollBack();
                        throw new LogicException('更新预占号订单失败', 202);
                    }
                    DB::commit();
                    echo 'success';
                    die;
                } catch (Exception $e) {
                    writeLogs($notify_data, "支付回调更新失败,失败code:" . $e->getCode() . ",失败msg:" . $e->getMessage(), 'notify'); // 写入日志记录
                    DB::rollBack();
                    echo 'fail';
                    die;
                }
            }
            echo "success";
            die;
        }
    }

    /**
     * 生成签名 - 重写该方法
     * @param array $data  配置对象
     *
     * @return float
     */
    public function query_weixin_sign($data)
    {
        $wxconfig = config("wechat.payment.default");
        //签名步骤一:按字典序排序参数
        ksort($data);
        $checkSign = $data['sign'];
        unset($data['sign']);
        $string = $this->to_url_params($data);
        //签名步骤二:在string后加入KEY
        $string = $string . "&key=".$wxconfig['key'];
        //签名步骤三:MD5加密或者HMAC-SHA256
        if(strlen($checkSign) <= 32){
            //如果签名小于等于32个,则使用md5验证
            $string = md5($string);
        } else {
            //是用sha256校验
            $string = hash_hmac("sha256",$string ,$wxconfig['key']);
        }
        //签名步骤四:所有字符转为大写
        $result = strtoupper($string);
        writeLogs(['sign'=> $result, 'checksign'=> $checkSign], '回调数据签名验证', 'notify'); // 写入日志记录
        if($result == $checkSign) {
            return true;
        } else {
            return false;
        }
    }

    public function from_xml($xml)
    {
        if (!$xml) {
            echo "xml数据异常!";
        }
        //将XML转为array  禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $data;
    }

    /**
     * 格式化参数格式化成url参数
     * @param array $datas 参数
     *
     * @return string
     */
    private function to_url_params($datas)
    {
        $buff = "";
        foreach ($datas as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }
}

  

一、使用场景以及说明

     使用场景:商户已有H5商城网站,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程。

 说明:1.用户打开图文消息或者扫描二维码,在微信内置浏览器打开网页进行的支付。

     2.商户网页前端通过使用微信提供的 JSAPI,调用微信支付模块。这种方式,适合需要在商户网页进行选购下单的购买流程。

二、准备工作

公共号支付需要提前在微信公共平台进行业务配置,包括设置支付授权目录测试支付目录和白名单设置JS接口安全域名以及设置授权回调页面域名

1.进行微信公众支付之前,我们需要申请个公众号,以及申请微信支付的功能。

微信支付方式区分第4张 

2.支付授权目录:

位置:微信支付——>开发配置——>公共号支付

1) 所有使用公众号支付方式发起支付请求的链接地址,都必须在支付授权目录之下;

2) 正式支付授权目录最多设置3个,测试授权目录最多设置1个,且域名必须通过ICP备案;==》截止目前(2017.11.15)该支付授权目录设置已经移动至

商户平台-->产品中心-->开发配置(传送门:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3),且最多可以同时设置5个目录

目录示例:

微信支付方式区分第5张      ||||||||    如果未设置授权目录或者目录不对,在手机微信调用jsapi时报错微信支付方式区分第6张  

3) 头部要包含http或https,须细化到二级或三级目录,以左斜杠“/”结尾。

业务中发起支付的页面地址必须在授权目录下,否则调用下单接口时会提示“当前页面的URL未注册”。

 微信支付方式区分第7张

微信支付方式区分第8张

这里再啰嗦地补充两点:

1)不使用框架的情况下,比如将官方sdk下载下来,改成demo放在根目录下,demo/example/jaspi.php,授权目录可参考 http://www. ×××.com/demo/example/

2 )使用框架的情况下,比如ThinkPHP, 项目名叫test ,目录结构如下,test/Application/Home/controller/WxpayController.class.php,授权目录可参考 http://www. ×××.com/index.php/Home/Wxpay/

3.JS接口安全域名:

位置:微信支付——>公共号设置——>功能设置——>JS接口安全域名

说明:设置JS接口安全域名后,公众号开发者可在该域名下调用微信开放的JS接口。

注意事项:

1) 可填写三个域名,要求是一级或一级以上域名(例:qq.com,或者 www.qq.com ),需使用字母、数字及“-”的组合,不支持IP地址及端口号;

2) 填写的域名须通过ICP备案的验证;

3)一个自然月内最多可修改并保存三次。

微信支付方式区分第9张

4.授权回调页面域名:(用来jsapi支付的必须)

位置:微信支付——>接口权限——>网页授权获取用户基本信息

用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。

注意事项:

1) 回调页面域名需使用字母、数字及“-”的组合,不支持IP地址及端口号。填写的域名需与实际回调URL中的域名相同;

2) 填写的域名须通过ICP备案的验证。

获取用户授权时redirect_uri对应的URL必须在此域名下,否则回调的地址会无法打开。

 微信支付方式区分第10张

 微信支付方式区分第11张

三、开发步骤

说明:整个微信公众号支付的流程如下:

【1】用户点击公众号内微信商城打开H5的支付页面

【2】H5页面通过JS调用微信支付接口

【3】微信服务器通过判断输入的JSON数据,返回给客户端相应的成功或失败信息

官方demo结构如下:

 微信支付方式区分第12张

1.JSAPI支付——H5网页端调起支付接口 

1)用户同意授权,获取code

请求链接:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#we-chat_redirect

 微信支付方式区分第13张

微信支付方式区分第14张

2)如果有code,直接就通过code能获取用户openid

如果没有code,通过createOauthUrlForCode方法,传入必要参数,获取code

3)如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数

redirect_uri?state=STATE

4)code说明以及scope的两种方式说明

【1】code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

这里获取到code 和 state(订单号) 后, 对订单进行验证,用户余额也进行验证,如果条件都满足则才能进行下面的流程。

【2】scope的两种方式说明:

微信提供了两种授权方式:snsapi_base和snsapi_userinfo。

snsapi_base:不弹出授权页面,直接跳转,只能获取用户openid;

snsapi_userinfo:弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息。

想要获取code,需要构造如下地址:

2. 通过code换取网页授权access_token (这里也获取到了openid)

1)请求链接:https://api.weixin.qq.com/sns/oauth2/access_token

?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

 微信支付方式区分第15张

微信支付方式区分第16张

微信支付方式区分第17张

2)获取openid:openid是微信支付jsapi支付接口必须的参数

【1】如果网页授权的作用域为snsapi_base(静默授权),则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

【2】如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。

 微信支付方式区分第18张

微信支付方式区分第19张

微信支付方式区分第20张

3)getOpenid方法中调用createOauthUrlForOpenid方法获取openid

微信支付方式区分第21张

3. 调用统一支付接口获取预付款id (这里有个小技巧:你如果直接把表单提交到这里,那就傻逼了。 你的服务器会和微信服务器交互2次,等拿到openid,你提交的数据早就被刷掉了,你可以把它放在session或者缓存里面,然后重定向到下面这个页面,等拿到openid后再从session里面拿数据。

官方文档说明:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

1) 设置必传参数,按照签名参数产生签名, 此时参与签名字段有 : appid, mch_id , nonce_str, openid, body , out_trade_no, total_fee, notify_url , trade_type

2) 将必传参数转成xml,createXml方法。

3) 使用xml,请求统一支付接口 https://api.mch.weixin.qq.com/pay/unifiedorder

4) 得到xml格式的返回值结果

5) 将结果转成数组,获取预支付id

微信支付方式区分第22张

4. 使用jsapi调起支付

1. 通过getParameters方法设置必传参数,接口输入数据为json

2. 根据官方demo中jsapi.php 调起支付,得到支付结果

 微信支付方式区分第23张

注:使用以上方式判断前端返回,res.err_msg 将在用户支付成功后返回 ok,但不保证它绝对可靠。

微信支付方式区分第24张

四、可能遇到的问题

1. 返回参数是xml,而不是直接输出success ,这个与微信app支付有区别。

2. 参与签名字段要保证一致,保证前后签名一样。

3. 由于存在重新发送后台通知的情况,因此同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。比如在支付成功后写入了支付日志,那么第二次回调前前判断是否已经有了支付日志,如果有,直接退出不作处理就好了。

4 . 当用户有余额,先用余额支付,剩下的才是微信 , 支付成功时,回调扣去余额

5. 如果同一笔订单号修改多次价格进行支付,第二次支付时,会出现生成预支付订单失败,主要原因是同一笔订单支付时,支付金额不能一样,或者给到第三方的订单号不一样,所以解决方法就是订单号+标志位(比如Z或A)+ 时间戳生成给到的第三方的支付号即可。

免责声明:文章转载自《微信支付方式区分》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇DotnetSpider爬虫简单示例 net coreES脑裂问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C# Xml序列化与反序列化

Xml文本的序列化与反序列化: public static class XmlSerializeHelper { // 序列化:对象 -> Xml文本 public static string SerializeToXmlString(object obj) {...

Java中InputStream输入流转String字符串的操作

目录 一、InputStream类中read方法 二、开源工具类IOUtils中toString方法 三、开源工具类CharStreams中toString方法 一、InputStream类中read方法 package com.zhiyin.test; import java.io.InputStream; public class MyT...

C#中ASCII码学习心得

1.利用调用ASCIIEncoding类来实现各种转换。如简单个ACS码和int转换。 ***利用(int)ASCIIEncoding类对象.GetBytes(character)[0]得到整数; public static int Asc(string character){if (character.Length == 1){ System.Text....

C#实现调用接口数据获取数据格式化XML、json转成Table的方法

废话不多说,直接上代码: json 格式化转DataTable: result为从接口得到的数据,格式化json的方法主要来自Newtonsoft.Json JObject joUnit = JObject.Parse(result);string a = Convert.ToString(joUnit["data"]["UnitComInfoList"]...

波场TRX 钱包开发,看这篇就够了

波场TRON作为一种基于区块链的去中心化内容协议,其目标意为通过区块链与分布式存储技术,构建一个全球范围内的自由内容娱乐体系,这个协议可以让每个用户自由发布,存储,拥有数据,并通过去中心化的自治形式,以数字资产发行,流通,交易方式决定内容的分发、订阅、推送,赋能内容创造者,形成去中心化的内容娱乐生态。   TRX是TRON区块链上账户的基本单位,所有其他代...

RocketMQ 参数详解

    NameServer配置属性 参数名 参数类型 描述 默认参数(时间为单位ms,数据单位为byte) rocketmqHome String RockerMQ主目录,默认用户主目录   namesrvAddr String NameServer地址   kvConfigPath String kv配置文件路径,包含顺序...