技术饭
实现小程序扫码发红包、小程序扫一物一码二维码领取红包、小程序红包
微信支付给用户发送包、转账的方式有两,一种是直接企业到零钱,另外一种是直接发红包,需要用户自行拆开领取,之前都是通过公众号直接支付发红包,现在的需求是需要在小程序里直接调起拆红包功能,或者是小程序里直接企业到零钱发个用户。
开发前请在微信支付中开启小程序红包功能:
产品简介
小程序红包是微信支付提供给商户在小程序场景下向用户发放红包的营销工具。区别于普通现金红包通过公众号消息推送红包的方式,小程序红包可以实现用户直接在小程序页面中领取红包,领取流程更加顺畅。用户领取红包后,资金到达用户微信支付零钱账户,和零钱包的其他资金有一样的使用出口;若用户未领取,资金将会在24小时后退回商户的微信支付账户中。
开通条件
1、即时入账且结算周期为T+1的商户,需满足1)入驻满90天,2)连续正常交易30天,3)保持正常健康交易。
2、其他结算周期的商户,暂无90/30天开通限制,但需保持正常健康交易。
3、商户选择开通权限的小程序未开通社交红包类目。
4、小程序主体不存在限制该功能使用的违规情况,包括但不限于:涉嫌欺诈,涉嫌违反相关法律法规等。
使用场景
目前仅支持用户微信扫码打开小程序时进行红包领取,其他场景暂不支持。(支持场景值1011,1025,1047,1124,小程序场景值详情参见文档)
小程序红包可在包括但不仅限于以下场景使用:
线下为小程序获取新用户、巩固老用户关系、提升用户活跃度;
商户线下营销活动,以小程序红包作为奖励,使营销活动更便利进行;
结合微信“一物一码”,利用小程序红包,精准营销触达目标用户。
示例
发放方式
小程序红包当前仅支持调用API的方式发放。
其他
1、首次开通时需要选择一个小程序进行权限申请,其他小程序可在产品设置中进行权限申请。
2、只有完成权限申请的小程序用户才可以在小程序中领取红包,故请确保实施前已经为相应小程序开通了权限。
3、使用场景相关限制从微信客户端7.0.5版本生效。了解更多“小程序码”和“一物一码” 介绍详见文档。
用微信小程序发红包的两种方法:
1,小程序开通了小程序红包的接口,但是目前该接口的的使用场景有限,仅限于场景值1011,1025,1047,1124时,基本为用户扫码打开小程序领取红包,这种方式的优势是有红包封面,有点击拆红包的按钮体验比较好,比较适合线下推广二维码时使用。开发时先后端调用发放红包接口,然后再传参给小程序端,调用领取红包接口wx.sendBizRedPacket方法即可。
2,调用微信支付的企业付款到零钱接口,微信支付的文档里是没有专门提到这种方式的,实际是用小程序的appid和用户之于小程序的openid来调用企业付款到零钱接口,这种方式的优势是现金直接划到用户零钱帐户,很多小程序做红包相关的活动时都是采用此种变通方式实现的。
开发前需要注意的点:
1)、小程序需要绑定微信支付,且绑定的微信支付商户号需开通现金红包,如果小程序是用公众号认证的,可以很方便的绑定认证微信公众号时开通的微信支付商户号。
2)、文档里公众号appid即小程序的appid,用户openid为用户对小程序的openid,该用户openid需在小程序里调用 wx.login() 获取临时登录凭证code,并回传到开发者服务器。 调用auth.code2Session接口,换取用户唯一标识OpenID和会话密钥session_key。
3)、调用发放红包接口,https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb 如果不填写'notify_way' => 'MINI_PROGRAM_JSAPI',不返回package,可以直接发现金红包,通知形式为服务通知,用户打开即可领取成功;如果填写notify_way则返回有package,只是package里的参数没有官方给的appid,但是有spid,但是不影响小程序正常领红包,用户依然可能通过领取红包接口成功领取现金红包。
4)、调用小程序领取红包接口成功领取红包,传给前端的参数中特别注意 'timeStamp' => time() . "",//时间戳,必须为字符串,增加.""转为字符串。
5)、小程序红包目前仅支持场景值1011,1025,1047,1124,包括扫描二维码,扫描小程序码和扫描一物一码等,具体可参与小程序红包文档。
PHP代码:
<?php
/**
* Author [ CopyLian ]
* Date: [ 2018.11.10 ]
* Email: [ copylian@aikehou.com ]
* Site: [ https://www.copylian.com/ ]
* Description: [ 企业付款到零钱、红包发送接口基类 ]
*/
namespace wechat;
class WechatMmpaymk {
//公众号APPID
private $appid;
//支付密钥签名
private $secret;
//IP
private $ip;
//微信接口域名
private $api_url;
//商户号
private $mchid;
/**
* [__construct 构造函数]
*/
public function __construct($prefix = '') {
//前缀
$prefix = !empty($prefix) ? $prefix : '';
//初始化数据
$this->appid = config('wechatpay.' . $prefix . 'pay_account.appid');
$this->secret = config('wechatpay.' . $prefix . 'pay_account.secret');
$this->mchid = config('wechatpay.' . $prefix . 'pay_account.mchid');
$this->ip = config('wechatpay.' . $prefix . 'pay_account.ip');
$this->api_url = config('wechatpay.' . $prefix . 'pay_account.api_url');
}
/**
* [xmltoarray xml格式转换为数组]
* @param [type] $xml [xml]
* @return [type] [xml 转化为array]
*/
public function xmltoarray($xml) {
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$val = json_decode(json_encode($xmlstring), true);
return $val;
}
/**
* [arraytoxml 将数组转换成xml格式(简单方法):]
* @param [type] $data [数组]
* @return [type] [array 转 xml]
*/
public function arraytoxml($data){
$str='<xml>';
foreach($data as $k=>$v) {
$str.='<'.$k.'>'.$v.'</'.$k.'>';
}
$str.='</xml>';
return $str;
}
/**
* [createNoncestr 生成随机字符串]
* @param integer $length [长度]
* @return [type] [字母大小写加数字]
*/
public function createNoncestr($length =32){
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYabcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for($i=0; $i < $length; $i++){
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* [curl_post_ssl 发送curl_post数据]
* @param [type] $url [发送地址]
* @param [type] $xmldata [发送文件格式]
* @param [type] $second [设置执行最长秒数]
* @param [type] $aHeader [设置头部]
* @return [type] [description]
*/
function curl_post_ssl($url, $vars, $second = 30, $aHeader = array()){
//证书位置
$isdir = __DIR__."/cert/";
//初始化curl
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置执行最长秒数
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// 终止从服务端进行验证
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//证书位置
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//证书位置
curl_setopt($ch, CURLOPT_CAINFO, 'PEM');
curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem');
if (count($aHeader) >= 1) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);//设置头部
}
curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);//全部数据使用HTTP协议中的"POST"操作来发送
$data = curl_exec($ch);//执行回话
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
echo "call faild, errorCode:$error\n";
curl_close($ch);
return false;
}
}
/**
* [sendMoneyToChange 企业付款到零钱]
* @param [type] $snedData [数据]
*/
public function sendMoneyToChange($snedData = []){
//总金额
if(empty($snedData) || !isset($snedData['order_sn']) || !isset($snedData['openid']) || !isset($snedData['amount']) || !isset($snedData['order_sn'])){
//返回数据
$rdata['code'] = 0;
$rdata['msg'] = '参数异常';
return $rdata;
}
//数据
$data = array(
'mch_appid' => $this->appid,//商户账号appid
'mchid' => $this->mchid,//商户号
'nonce_str' => $this->createNoncestr(),//随机字符串
'partner_trade_no' => $snedData['order_sn'],//商户订单号
'openid' => $snedData['openid'],//用户openid
'check_name' => isset($snedData['check_name']) ? $snedData['check_name'] : 'NO_CHECK',//校验用户姓名选项,
're_user_name' => isset($snedData['re_user_name']) ? $snedData['re_user_name'] : '',//收款用户姓名
'amount' => $snedData['amount'],//金额
'desc' => $snedData['desc'],//企业付款描述信息
'spbill_create_ip' => $this->ip //Ip地址
);
//生成签名算法
$secrect_key = $this->secret;///这个就是个API密码。MD5 32位。
$data = array_filter($data);
ksort($data);
$str = '';
foreach($data as $k=>$v) {
$str .= $k . '=' . $v . '&';
}
$str .= 'key=' . $secrect_key;
$data['sign'] = md5($str);
//生成签名算法
$xml = $this->arraytoxml($data);
$url = $this->api_url . 'mmpaymkttransfers/promotion/transfers'; //调用接口
$res = $this->curl_post_ssl($url,$xml);
$return = $this->xmltoarray($res);
//返回数据
$rdata['code'] = 1;
$rdata['msg'] = '提交成功';
$rdata['data'] = $return;
return $rdata;
}
/**
* [queryChange 查询企业付款到零钱]
* @param [type] $snedData [数据]
*/
public function queryChange($snedData = []){
//总金额
if(empty($snedData) || !isset($snedData['order_sn'])){
//返回数据
$rdata['code'] = 0;
$rdata['msg'] = '参数异常';
return $rdata;
}
//数据
$data = array(
'appid' => $this->appid,//商户账号appid
'mch_id' => $this->mchid,//商户号
'nonce_str' => $this->createNoncestr(),//随机字符串
'partner_trade_no' => $snedData['order_sn']//商户订单号
);
//生成签名算法
$secrect_key = $this->secret;///这个就是个API密码。MD5 32位。
$data = array_filter($data);
ksort($data);
$str = '';
foreach($data as $k=>$v) {
$str .= $k . '=' . $v . '&';
}
$str .= 'key=' . $secrect_key;
$data['sign'] = md5($str);
//生成签名算法
$xml = $this->arraytoxml($data);
$url = $this->api_url . 'mmpaymkttransfers/gettransferinfo'; //调用接口
$res = $this->curl_post_ssl($url,$xml);
$return = $this->xmltoarray($res);
//返回数据
$rdata['code'] = 1;
$rdata['msg'] = '提交成功';
$rdata['data'] = $return;
return $rdata;
}
/**
* [redPacketPay 现金红包]
* @param [type] $snedData [数据]
*/
public function redPacketPay($snedData = []){
//总金额
if(empty($snedData) || !isset($snedData['order_sn']) || !isset($snedData['openid']) || !isset($snedData['amount']) || !isset($snedData['order_sn'])){
//返回数据
$rdata['code'] = 0;
$rdata['msg'] = '参数异常';
return $rdata;
}
//数据
$data = array(
'wxappid' => $this->appid,//商户账号appid
'mch_id' => $this->mchid,//商户号
'nonce_str' => $this->createNoncestr(),//随机字符串
'mch_billno' => $snedData['order_sn'],//商户订单号
're_openid' => $snedData['openid'],//用户openid
'act_name' => isset($snedData['act_name']) ? $snedData['act_name'] : '红包活动', //活动名称
'send_name' => isset($snedData['send_name']) ? $snedData['send_name'] : '多想互动',//红包发送者名称
'wishing' => isset($snedData['wishing']) ? $snedData['wishing'] : '恭喜发财', //红包祝福语
'remark' => isset($snedData['remark']) ? $snedData['remark'] : '抢到红包了,恭喜发财!', //备注
'total_num' => 1, //红包发放总人数
'total_amount' => $snedData['amount'],//金额
'client_ip' => $this->ip, //Ip地址
'scene_id' => 'PRODUCT_2' //场景id,发放红包使用场景,红包金额大于200或者小于1元时必传 PRODUCT_1:商品促销;PRODUCT_2:抽奖;PRODUCT_3:虚拟物品兑奖 ;PRODUCT_4:企业内部福利;PRODUCT_5:渠道分润;PRODUCT_6:保险回馈;PRODUCT_7:彩票派奖;PRODUCT_8:税务刮奖
);
//生成签名算法
$secrect_key = $this->secret;///这个就是个API密码。MD5 32位。
$data = array_filter($data);
ksort($data);
$str = '';
foreach($data as $k=>$v) {
$str .= $k . '=' . $v . '&';
}
$str .= 'key=' . $secrect_key;
$data['sign'] = md5($str);
//生成签名算法
$xml = $this->arraytoxml($data);
$url = $this->api_url . 'mmpaymkttransfers/sendredpack'; //调用接口
$res = $this->curl_post_ssl($url,$xml);
$return = $this->xmltoarray($res);
//返回数据
$rdata['code'] = 1;
$rdata['msg'] = '提交成功';
$rdata['data'] = $return;
return $rdata;
}
/**
* [queryRedPacket 查询现金红包]
* @param [type] $snedData [数据]
*/
public function queryRedPacket($snedData = []){
//总金额
if(empty($snedData) || !isset($snedData['order_sn'])){
//返回数据
$rdata['code'] = 0;
$rdata['msg'] = '参数异常';
return $rdata;
}
//数据
$data = array(
'appid' => $this->appid,//商户账号appid
'mch_id' => $this->mchid,//商户号
'nonce_str' => $this->createNoncestr(),//随机字符串
'mch_billno' => $snedData['order_sn'], //商户订单号
'bill_type' => 'MCHT', //MCHT:通过商户订单号获取红包信息。
);
//生成签名算法
$secrect_key = $this->secret;///这个就是个API密码。MD5 32位。
$data = array_filter($data);
ksort($data);
$str = '';
foreach($data as $k=>$v) {
$str .= $k . '=' . $v . '&';
}
$str .= 'key=' . $secrect_key;
$data['sign'] = md5($str);
//生成签名算法
$xml = $this->arraytoxml($data);
$url = $this->api_url . 'mmpaymkttransfers/gethbinfo'; //调用接口
$res = $this->curl_post_ssl($url,$xml);
$return = $this->xmltoarray($res);
//返回数据
$rdata['code'] = 1;
$rdata['msg'] = '提交成功';
$rdata['data'] = $return;
return $rdata;
}
/**
* [sendMiniprogramRedPacket 发送小程序红包]
* @param [type] $snedData [数据]
*/
public function sendMiniprogramRedPacket($snedData = []){
//总金额
if(empty($snedData) || !isset($snedData['order_sn']) || !isset($snedData['openid']) || !isset($snedData['amount']) || !isset($snedData['order_sn'])){
//返回数据
$rdata['code'] = 0;
$rdata['msg'] = '参数异常';
return $rdata;
}
//数据
$data = array(
'wxappid' => $this->appid,//商户账号appid
'mch_id' => $this->mchid,//商户号
'nonce_str' => $this->createNoncestr(),//随机字符串
'mch_billno' => $snedData['order_sn'],//商户订单号
're_openid' => $snedData['openid'],//用户openid
'act_name' => isset($snedData['act_name']) ? $snedData['act_name'] : '红包活动', //活动名称
'send_name' => isset($snedData['send_name']) ? $snedData['send_name'] : '多想互动',//红包发送者名称
'wishing' => isset($snedData['wishing']) ? $snedData['wishing'] : '恭喜发财', //红包祝福语
'remark' => isset($snedData['remark']) ? $snedData['remark'] : '抢到红包了,恭喜发财!', //备注
'total_num' => 1, //红包发放总人数
'total_amount' => $snedData['amount'],//金额
'notify_way' => "MINI_PROGRAM_JSAPI",
'scene_id' => 'PRODUCT_2' //场景id,发放红包使用场景,红包金额大于200或者小于1元时必传 PRODUCT_1:商品促销;PRODUCT_2:抽奖;PRODUCT_3:虚拟物品兑奖 ;PRODUCT_4:企业内部福利;PRODUCT_5:渠道分润;PRODUCT_6:保险回馈;PRODUCT_7:彩票派奖;PRODUCT_8:税务刮奖
);
//生成签名算法
$secrect_key = $this->secret;///这个就是个API密码。MD5 32位。
$data = array_filter($data);
ksort($data);
$str = '';
foreach($data as $k=>$v) {
$str .= $k . '=' . $v . '&';
}
$str .= 'key=' . $secrect_key;
$data['sign'] = md5($str);
//生成签名算法
$xml = $this->arraytoxml($data);
$url = $this->api_url . 'mmpaymkttransfers/sendminiprogramhb'; //调用接口
$res = $this->curl_post_ssl($url,$xml);
$return = $this->xmltoarray($res);
//测试数据
// $return['return_code'] = 'SUCCESS';
// $return['result_code'] = 'SUCCESS';
// $return['package'] = 'sendid%3D1fd4eaa593ddb76542d36dc8ff6dc009ad5b5e3ac187d925c10cc8f1e4ac775c%26ver%3D8%26sign%3D19df1785d835365c9e869e4cef9f9b11c382e0ac21d83ae42bd0e94172fcd136e0ba93b7acca576aac713874e316d90a114f71c2240fb1a6e36c1cc866cb0a2a%26mchid%3D43452347%26spid%3D1487631232';
//返回拆红包数据
$program_data_return = [
'appId' => $this->appid, //商户账号appid
'timeStamp' => time() . "",
'nonceStr' => $this->createNoncestr(),
'signType' => 'MD5',
'paySign' => ''
];
if($return['return_code'] == 'SUCCESS' && $return['result_code'] == 'SUCCESS'){
if(isset($return['package']) && !empty($return['package'])){
//设置参数
$return['package'] = urlencode($return['package']);
//红包拆参数
$program_data = [
'appId' => $program_data_return['appId'],
'timeStamp' => $program_data_return['timeStamp'],
'nonceStr' => $program_data_return['nonceStr'],
'package' => $return['package']
];
//生成签名算法
$program_data = array_filter($program_data);
ksort($program_data);
$program_data_str = '';
foreach($program_data as $p_k=>$p_v) {
$program_data_str .= $p_k . '=' . $p_v . '&';
}
$program_data_str .= 'key=' . $this->secret;
$program_data_return['paySign'] = md5($program_data_str);
}
}
//小程序红包数据
$return['program_data'] = $program_data_return;
//返回数据
$rdata['code'] = 1;
$rdata['msg'] = '提交成功';
$rdata['data'] = $return;
return $rdata;
}
}
小程序调起拆红包代码:
var scenedata = wx.getLaunchOptionsSync()//获取场景代码
if (scenedata.scene == 1047 || scenedata.scene == 1011 || scenedata.scene == 1025 || scenedata.scene == 1124){
wx.request({
url: '域名/test/sendMiniprogramRedPacket', //仅为示例,并非真实的接口地址
data: {
o: 'okMoC5dsKK54HfcEC-D9fOMxkVu8',
m: '0.3'
},
header: {
'content-type': 'application/json' // 默认值
},
success (res) {
var rdata = res.data.data;
console.log(rdata)
wx.sendBizRedPacket ({
timeStamp: rdata.program_data.timeStamp, // 支付签名时间戳,
nonceStr: rdata.program_data.nonceStr, // 支付签名随机串,不长于 32 位
package: rdata.package, //扩展字段,由商户传入
signType: rdata.program_data.signType, // 签名方式,
paySign: rdata.program_data.paySign, // 支付签名
success:function(res1){
console.log(res1)
},
fail:function(res1){},
complete:function(res1){}
})
}
})
}
参考:
小程序营销利器,wx.sendBizRedPacket小程序红包完整详细开发代码,小程序中直接领取微信红包,无需任何资质
文明上网理性发言!