小程序开发----自定义会员卡卡号和用户领取,以及简单介绍卡券功能和注意事项
前一段时间做项目涉及到这方面的内容,看了技术文档,小程序页面没有详细介绍,要前往微信公众号开发文档,卡券功能是先为微信公众号开发的功能,后来也提供个小程序,文档在小程序中没有过多的介绍,微信文档我就不想过多的吐槽了,大家都懂。
一、会员卡
1、在小程序会员卡,其实也是用到微信公众号的,开始在摸索,在微信公众号管理后台创建会员卡,在小程序中能调用,但会员卡号不能自定义编号,因为管理后创建的,默认是不能自定义编码(use_custom _code=false),一键开卡(wx_activate=true)、自动激活(auto_activate=true),所以不是我们需要的,应该另辟一条道路,自己通过接口创建会员卡,代码例子如下:
1 public function addCard(){
2 $url = \'https://api.weixin.qq.com/card/create?access_token=\'.$this->getAccessToken(); 4 $post[\'card\'][\'card_type\'] = \'MEMBER_CARD\'; 5 $post[\'card\'][\'member_card\'][\'prerogative\'] = \'111\'; 6 $post[\'card\'][\'member_card\'][\'auto_activate\'] = true; //自动激活 7 $post[\'card\'][\'member_card\'][\'wx_activate\'] = true; //一键开卡 8 $post[\'card\'][\'member_card\'][\'supply_bonus\'] = false; 9 $post[\'card\'][\'member_card\'][\'supply_balance\'] = false; 10 $post[\'card\'][\'member_card\'][\'base_info\'][\'logo_url\'] = "http://mmbiz.qpic.cn/mmbiz_png/xxxxxxxxxxxxxxxxxxx=png"; //会员卡图片路径 11 $post[\'card\'][\'member_card\'][\'base_info\'][\'code_type\'] = "CODE_TYPE_TEXT"; 12 $post[\'card\'][\'member_card\'][\'base_info\'][\'brand_name\'] = \'brand_name_1\'; 13 $post[\'card\'][\'member_card\'][\'base_info\'][\'title\'] = \'title_1\'; 14 $post[\'card\'][\'member_card\'][\'base_info\'][\'color\'] = \'Color010\'; 15 $post[\'card\'][\'member_card\'][\'base_info\'][\'notice\'] = \'notice\'; 16 $post[\'card\'][\'member_card\'][\'base_info\'][\'description\'] = \'description\'; 17 $post[\'card\'][\'member_card\'][\'base_info\'][\'sku\'][\'quantity\'] = 1000000000; 18 $post[\'card\'][\'member_card\'][\'base_info\'][\'date_info\'][\'type\'] = \'DATE_TYPE_PERMANENT\'; 19 $post[\'card\'][\'member_card\'][\'base_info\'][\'get_limit\'] = 1; //限领一张 20 $post[\'card\'][\'member_card\'][\'base_info\'][\'can_share\'] = false; 21 $post[\'card\'][\'member_card\'][\'base_info\'][\'can_give_friend\'] = false; 22 $post[\'card\'][\'member_card\'][\'base_info\'][\'use_custom_code\'] = true; //开启自定义编码 23 $data = http_curl_post_json($url,$post); 24 $data = json_decode($data,true); 25 return $data; 26 }
2、创建好会员卡后,在小程序中写入用户领取会员卡的前端代码,jssdk的wx.addCard方法,其中加密不同于jssdk的加密方式,加密方式看微信公众号jssdk说明文档附录4,addCard方法的cardExt
里的参数很重要
code:需要给用户自定义编码时,才需填入你后台开发生成编码(会员卡卡号),否则不填为空字符串
openid:需要给自定的用户发放会员卡是,才需填入用户的openid(微信公众号的openid而不是小程序的openid),否则为空字符串
...........等参数
注意:以上参数需要用到的时候才加入一起加密,如:code的值为空字符串则不需加了加密,否则领取会员卡时会出签名错误
加密方法如下:
1 <?php 2 class Jssdk { 3 4 private $appId = \'\'; 5 private $appSecret = \'\'; 6 public function __construct($appId,$appSecret){ 7 $this->appId = $appId; 8 $this->appSecret = $appSecret; 9 } 10 /* 11 *实现微信jssdk加密 12 */ 13 public function getSignPackage($url) { 14 $jsapiTicket = $this->getJsApiTicket(); 15 // 注意 URL 一定要动态获取,不能 hardcode. //ajax页面url则必须要前端传输url地址 16 // $protocol = (!empty($_SERVER[\'HTTPS\']) && $_SERVER[\'HTTPS\'] !== \'off\' || $_SERVER[\'SERVER_PORT\'] == 443) ? "https://" : "http://"; 17 // $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; 18 $timestamp = time(); 19 $nonceStr = $this->createNonceStr(); 20 21 // 这里参数的顺序要按照 key 值 ASCII 码升序排序 22 $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url"; 23 24 $signature = sha1($string); 25 26 $signPackage = array( 27 "appId" => $this->appId, 28 "nonceStr" => $nonceStr, 29 "timestamp" => $timestamp, 30 "url" => $url, 31 "signature" => $signature, 32 "rawString" => $string 33 ); 34 return $signPackage; 35 } 36 37 //获取access_token 38 public function getAccessTokenInfo(){ 39 return $this->getAccessToken(); 40 } 41 42 private function createNonceStr($length = 16) { 43 $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 44 $str = ""; 45 for ($i = 0; $i < $length; $i++) { 46 $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 47 } 48 return $str; 49 } 50 51 private function getJsApiTicket() { 52 // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例 53 $data = json_decode(file_get_contents("xxxx/jsapi_ticket.json")); //xxxx/jsapi_ticket.json 是存放路径 54 if ($data->expire_time < time()) { 55 $accessToken = $this->getAccessToken(); 56 $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken"; 57 $res = json_decode($this->curl($url)); 58 $ticket = $res->ticket; 59 if ($ticket) { 60 $data->expire_time = time() + 7000; 61 $data->jsapi_ticket = $ticket; 62 $fp = fopen("xxxx/jsapi_ticket.json", "w"); 63 fwrite($fp, json_encode($data)); 64 fclose($fp); 65 } 66 } else { 67 $ticket = $data->jsapi_ticket; 68 } 69 return $ticket; 70 } 71 72 private function getAccessToken() { 73 // access_token 应该全局存储与更新,以下代码以写入到文件中做示例 74 $data = json_decode(file_get_contents("xxxx/access_token.json")); 75 if ($data->expire_time < time()) { 76 $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret"; 77 $res = json_decode($this->curl($url)); 78 $access_token = $res->access_token; 79 if ($access_token) { 80 $data->expire_time = time() + 7000; 81 $data->access_token = $access_token; 82 $fp = fopen("xxxx/access_token.json", "w"); 83 fwrite($fp, json_encode($data)); 84 fclose($fp); 85 } 86 } else { 87 $access_token = $data->access_token; 88 } 89 return $access_token; 90 } 91 92 private function curl( $url , $postFields = NULL ) 93 { 94 $ch = curl_init(); 95 curl_setopt( $ch , CURLOPT_TIMEOUT , 3 ); 96 curl_setopt( $ch , CURLOPT_URL , $url ); 97 curl_setopt( $ch , CURLOPT_FAILONERROR , FALSE ); 98 curl_setopt( $ch , CURLOPT_RETURNTRANSFER , TRUE ); 99 //https 请求 100 if ( strlen( $url ) > 5 && strtolower( substr( $url , 0 , 5 ) ) == \'https\' ){ 101 curl_setopt( $ch , CURLOPT_SSL_VERIFYPEER , FALSE ); 102 curl_setopt( $ch , CURLOPT_SSL_VERIFYHOST , FALSE ); 103 } 104 105 if ( is_array( $postFields ) && 0 < count( $postFields ) ){ 106 $postBodyString = \'\'; 107 $postMultipart = FALSE; 108 foreach ( $postFields as $k => $v ) { 109 if ( \'@\' != substr( $v , 0 , 1 ) ) //判断是不是文件上传 110 { 111 $postBodyString .= "$k=" . urlencode( $v ) . "&"; 112 } else { 113 //文件上传用multipart/form-data,否则用www-form-urlencoded 114 $postMultipart = TRUE; 115 } 116 } 117 $postFields = trim( $postBodyString , \'&\' ); 118 unset( $k , $v ); 119 curl_setopt( $ch , CURLOPT_POST , TRUE ); 120 if ( $postMultipart ){ 121 curl_setopt( $ch , CURLOPT_POSTFIELDS , $postFields ); 122 } else { 123 curl_setopt( $ch , CURLOPT_POSTFIELDS , $postFields ); 124 } 125 } 126 127 $reponse = curl_exec( $ch ); 128 curl_close( $ch ); 129 return $reponse; 130 } 131 132 //卡券等签名, 133 private function apiTicket() { 134 // api_ticket 应该全局存储与更新,以下代码以写入到文件中做示例 135 $data = json_decode(file_get_contents("xxxx/api_ticket.json")); 136 if ($data->expires_in < time()) { 137 $accessToken = $this->getAccessToken(); 138 $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=$accessToken&type=wx_card"; 139 $res = json_decode($this->curl($url)); 140 $ticket = $res->ticket; 141 if ($ticket) { 142 $data->expires_in = time() + 7000; 143 $data->ticket = $ticket; 144 $fp = fopen("xxxx/api_ticket.json", "w"); 145 fwrite($fp, json_encode($data)); 146 fclose($fp); 147 } 148 } else { 149 $ticket = $data->ticket; 150 } 151 return $ticket; 152 } 153 //由于历史原因,卡券的JS接口先于JSSDK出现,当时的JSAPI并没有鉴权体系,所以在卡券的签名里也加上了appsecret/api_ticket这些身份信息 154 public function getApiTicket($cardId=\'\',$code=\'\') { 155 $apiTicket = $this->apiTicket(); 156 $timestamp = time(); 157 $nonceStr = $this->createNonceStr(); 158 // 这里参数的顺序要按照 key 值 ASCII 码升序排序 159 $tmpArr = array($nonceStr,$apiTicket,$cardId,$timestamp); 160 if($code){ 161 array_push($tmpArr, $code); 162 } 163 sort($tmpArr,SORT_STRING); 164 $tmpStr = implode($tmpArr); 165 $signature = sha1($tmpStr); 166 $signPackage = array( 167 "cardId" => $cardId, 168 "nonceStr" => $nonceStr, 169 "timestamp" => $timestamp, 170 "signature" => $signature, 171 ); 172 return $signPackage; 173 } 174 } 175 ?>
3、领取会员卡,回调给后台领取信息,在addCard方法领取成功会返回用户领取的相关信息,success中把领取的信息提交后台保存,从而确认领取成功。
4、很多jssdk功能最先在微信公众号的,之后小程序在这基础之上引用,所以在小程序引用jssdk的方法涉及到的appid和appid密码都是微信公众号的而不是小程序的,卡券也如此,千万不要搞错,否则会造成签名错误等问题,这点微信官网没有说明。
5、熟悉会员卡,其它卡券类型也差不多。
以下会运用到的方法:
1 //post方式提交json格式的数据 2 function http_curl_post_json($url,$post){ 3 $data_string = json_encode($post); 4 $ch = curl_init($url); 5 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); 6 curl_setopt($ch, CURLOPT_POSTFIELDS,$data_string); 7 curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); 8 curl_setopt($ch, CURLOPT_HTTPHEADER, array( 9 \'Content-Type: application/json\', 10 \'Content-Length: \' . strlen($data_string)) 11 ); 12 $result = curl_exec($ch); 13 return $result; 14 }
是