微信代金券的注意事项
1.直连商户或者服务商需要登录微信商户后台开通预充值营销插件和免充值营销插件
2.公众账号的appid 与商户号关联绑定关系
3.小程序需要小程序appid与商户号关联绑定
4.需要服务商或者直连商户的cert证书的序列号key证书的在服务器的据对路径
5.如果是子商户发放服务商创建的代金券还需要子商户的cert证书与key证书在服务器的绝对路径,
6.子商户发放服务商创建的代金券还需要向微信发送邮件申请跨商户发券权限 :
Q:商户号A制券,允许商户号B发券,如何配置?(例如服务商制券,在子商户的场景例如公众号、小程序里发券)
A:跨商户号发券的使用场景是,A商户号制券,在B商户的场景中发券,例如小程序。需要A开通跨商户发券制券商户号的白名单、B开通商户发券发券商户号的白名单。然后A开始制券,制券是要把B添加为“可发券商户”。然后再用B的商户号调用接口,在和B商户号有绑定关系的APPID对应的场景中发券,就可以了。
目前申请流程:
需要先开通跨商户号发券权限。发邮件给v_jjinglliu@tencent.com,anthonylin@tencent.com,说明申请原因、制券的商户号、发券商户号。邮件标题“申请跨商户号发券+商户名称”。配置批次的时候把可发券商户号填发券商户,然后调用发券接口即可。申请后会在一个工作日内开通并回复邮件。
注意(需要把发券商户子商户放在批次的可发券商户号中)
7.创建代金券时no_cash 为true时是免充值类型,false为预充值类型
8.如果是服务商创建代金券时available_merchants 应该是可核销代金券的子商户集合
注意商户A制券B商户发放,服务商是没有分润,服务商制券,服务商发券这个是有分润的
9.公共类
<?php
namespace Addons\Pay;
class WxPayV3
{
protected $authorization = 'WECHATPAY2-SHA256-RSA2048'; //认证类型
protected $method = "POST";
protected $url; //链接
protected $mch_id; //商户号
protected $nonce_str; //随机字符串
protected $sign; //签名
protected $timestamp; //时间
protected $serial_no; //商户Api证书序列号
protected $apiclient_key; //私钥地址
protected $apiclient_cert; //公钥地址
protected $token; //Token
protected $data; //发送参数
protected $stock_id; //批次号
protected $response; //返回信息
protected $image_type; //图片类型
protected $boundary; //边界符
protected $suffix; //图片后缀
protected $openid; //发放的openid
protected $file; //图片信息
public function __construct($param)
{
$this->mch_id = isset($param['mch_id']) ? $param['mch_id'] : '';
$this->data = isset($param['data']) ? $param['data'] : '';
$this->stock_id = isset($param['stock_id']) ? $param['stock_id'] : '';
$this->serial_no = isset($param['serial_no']) ? $param['serial_no'] : '';
$this->apiclient_key = isset($param['apiclient_key']) ? $param['apiclient_key'] : '';
$this->image_type = isset($param['image_type']) ? $param['image_type'] : '';
$this->boundary = isset($param['boundary']) ? $param['boundary'] : '';
$this->openid = isset($param['openid']) ? $param['openid'] : '';
$this->file = isset($param['file']) ? $param['file'] : '';
}
/**
* 上传图片
* @return bool|string
*/
public function upload()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/marketing/favor/media/image-upload';
$result = $this->uploadRequestAction();
return $result;
}
/**
* 图片上传请求
* @return bool|string
*/
public function uploadRequestAction()
{
if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
}
$headerParam = $this->uploadHeaderParam(); //获取头部信息
$boundarystr = "--{$this->boundary}\r\n";// $out是post的内容
$str = $boundarystr;
$str .= 'Content-Disposition: form-data; name="meta"' . "\r\n";
$str .= 'Content-Type: application/json' . "\r\n";
$str .= "\r\n";
$str .= json_encode($this->data) . "\r\n";
$str .= $boundarystr;
$str .= 'Content-Disposition: form-data; name="file"; filename="' . $this->data['filename'] . '"' . "\r\n";
$str .= 'Content-Type: ' . $this->image_type . ";\r\n";
$str .= "\r\n";
$str .= $this->file . "\r\n";
$str .= $boundarystr . "--\r\n";
// print_r($str);exit;
$this->response = $this->http_Request($this->url, $headerParam, $str);
return $this->response;
}
/**
* 图片上传头部参数
* @return array
*/
public function uploadHeaderParam()
{
$this->getSign(); //生成签名
$this->getToken(); //生成Token
$header = [
"Content-Type: multipart/form-data;name=meta",
"Content-Type: application/json",
"User-Agent:" . $_SERVER['HTTP_USER_AGENT'],
'Authorization: ' . $this->authorization . ' ' . $this->token,
"Content-Type: multipart/form-data;boundary=" . $this->boundary
];
return $header;
}
/**
* 创建代金券
* @return bool|string
*/
public function createCoupon()
{
$this->url = 'https://api.mch.weixin.qq.com/v3/marketing/favor/coupon-stocks'; //创建代金券请求链接
$result = $this->requestAction();
return $result;
}
/**
* 激活代金券
* @return bool|string
*/
public function activateCoupon()
{
$this->url = "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/{$this->stock_id}/start";
$result = $this->requestAction();
return $result;
}
/**
* 发放代金券
*/
public function grant()
{
$this->url = "https://api.mch.weixin.qq.com/v3/marketing/favor/users/{$this->openid}/coupons";
$result = $this->requestAction();
return $result;
}
/**
* 暂停代金券
*/
public function suspend()
{
$this->url = "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/{$this->stock_id}/pause";
$result = $this->requestAction();
return $result;
}
/**
* 重启代金券
*/
public function restart()
{
$this->url = "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/{$this->stock_id}/restart";
$result = $this->requestAction();
return $result;
}
/**
* 发送请求
* @return bool|string
*/
protected function requestAction()
{
if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
}
$headerParam = $this->getHeaderParam(); //获取头部信息
// print_r($headerParam);
$this->response = $this->http_Request($this->url, $headerParam, json_encode($this->data));
// print_r(json_decode($this->response));
return $this->response;
}
/**
* 获取请求头部参数
* @return array
*/
protected function getHeaderParam()
{
$this->getSign(); //生成签名
$this->getToken(); //生成Token
$header = [
"Content-type: application/json;charset='utf-8'",
"Accept:application/json",
"User-Agent:*/*",
'Authorization: ' . $this->authorization . ' ' . $this->token,
];
return $header;
}
/**
* 生成签名
*/
protected function getSign()
{
$url_parts = parse_url($this->url); //链接
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$this->timestamp = time();
$this->nonce_str = createKey(); //随机字符串
$message = $this->method . "\n" .
$canonical_url . "\n" .
$this->timestamp . "\n" .
$this->nonce_str . "\n" .
json_encode($this->data) . "\n";
openssl_sign($message, $raw_sign, openssl_get_privatekey(file_get_contents($this->apiclient_key)), 'sha256WithRSAEncryption');
$this->sign = base64_encode($raw_sign);
}
/**
* 生成Token
* @return string
*/
protected function getToken()
{
$this->token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
$this->mch_id, $this->nonce_str, $this->timestamp, $this->serial_no, $this->sign);
}
/**
* 数据请求
* @param $url
* @param array $header 获取头部
* @param string $post_data POST数据,不填写默认以GET方式请求
* @return bool|string
*/
public function http_Request($url, $header = array(), $post_data = "")
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 2);
if ($post_data != "") {
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //设置post提交数据
}
//判断当前是不是有post数据的发
$response = curl_exec($ch);
if ($response === FALSE) {
$response = "curl 错误信息: " . curl_error($ch);
}
curl_close($ch);
return $response;
}
}