Sandpay.php 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. <?php
  2. namespace app\common\library;
  3. use function EasyWeChat\Kernel\data_to_array;
  4. /**
  5. * 杉德支付
  6. */
  7. class Sandpay
  8. {
  9. //账号
  10. protected $mer_no = '';
  11. protected $mer_key = '';
  12. protected $domain_url = '';
  13. public function __construct(){
  14. $this->mer_no = '6888806122762';//商户号 6888806122762[正式] 68888TS122762[测试]
  15. $this->mer_key = '6mksKr1IsfU0kTD9DNayV67cvfYJVQWLOOLYwJX1rOq5DJVsppgr07JYEMG5g+4hgx/Dlc/pW00=';//未使用
  16. $this->business_private = APP_PATH.'common/library/sandpay/prdnew.pfx';//私钥 prd.pfx[正式] 68888TS122762.pfx[测试]
  17. $this->sandpay_public = APP_PATH.'common/library/sandpay/prdnew.cer';//公钥 sand-prd-prd.cer[正式] sand-test-test.cer[测试]
  18. $this->sandpublic = APP_PATH.'common/library/sandpay/sand.cer';//衫德公钥
  19. $this->privateKeyPwd = 'jin860815';//私钥密码 jin860815[正式] 123456[测试]
  20. $this->domain_url = config('domain_url');
  21. $this->h5_url = config('h5_url');
  22. $this->md5key = 'kYgEWLWLOUxnxHgcqzdIwvNT9uGPGAAdfe7HyxdHNxa6dfGXO4+DEF+oWkvKRuAcS0h5VeRxfu6SZNbopT9Gz77aAb//j5EciX8mUUtf+8P0ELLrObXih1KbHnhxHKtA6VEMOSJPtypixuz+UKFTiw==';//未使用
  23. }
  24. /**
  25. * 衫德微信收款
  26. * https://www.yuque.com/sd_cw/xfq1vq/ut7292
  27. * @return array
  28. */
  29. public function wechat($params=[]){
  30. $result = [
  31. 'status' => 1,
  32. 'msg' => '',
  33. 'data' => [],
  34. ];
  35. try {
  36. //统一下单参数
  37. $time = time();
  38. $createTime = date('YmdHis',$time);
  39. /*wx_app_id:移动应用Appid(微信开放平台获取,wx开头)gh_ori_id:小程序原始id(微信公众平台获取,gh_开头)
  40. path_url:拉起小程序页面默认地址 miniProgramType:开发时根据小程序是开发版、体验版或正式版自行选择。正式版:0; 开发版:1; 体验版:2*/
  41. $payExtra = ['wx_app_id'=>'wx64e2709ce8095ab6','gh_ori_id'=>'gh_be8042ce502b','path_url'=>'pages/zf/index?','miniProgramType'=>'2'];
  42. $ip = request()->ip();
  43. $ipStr = str_replace('.','_',$ip);
  44. $orderNo = isset($params['order_no']) ? $params['order_no'] : 'P23082914483724021875';
  45. $goodsName = isset($params['goods_name']) ? $params['goods_name'] : 'test';
  46. $money = isset($params['money']) ? $params['money'] : "0.1";
  47. $type = isset($params['type']) ? $params['type'] : 'gold';//vip=会员,gold=充值
  48. $extend = ['type' => $type];
  49. $payExtraStr = json_encode($payExtra);
  50. $payExtraStr = stripslashes($payExtraStr);
  51. //$payExtraStr = str_replace('\\','',$payExtraStr);
  52. $paramsData = [
  53. 'version' => '10',//版本号 默认为10
  54. 'mer_no' => $this->mer_no,//商户号 商户号是68888开头13位
  55. 'mer_order_no' => $orderNo,//商户订单号 自定义,最小长度12位 到30位
  56. 'create_time' => $createTime,//订单创建时间 yyyyMMddHHmmss例 20180813142345
  57. 'order_amt' => $money,//订单金额 例:"order_amt"="0.11" 单位: 元 //部分产品金额有最小额度限制,建议设置大于0.1元
  58. 'notify_url' => 'https://zhiliao.huxiukeji.com/api/sandpay/notify',//异步通知地址 例http://sandcash/notify 主动通知商户充转提付、开户签约结果的https路径。通知地址必须为直接可以访问的URL。该地址需向杉德报备。回调地址规则:https://cshall.sandpay.com.cn/knowledge/detail/10390
  59. 'create_ip' => $ipStr,//客户端IP 用户所在客户端的真实ip其中的“.”替换为“_” 。例192_168_0_1。
  60. 'pay_extra' => $payExtraStr,//支付扩展域
  61. 'accsplit_flag' => 'NO',//分账标识 例:NO 无分账:NO;有分账:YES
  62. 'sign_type' => 'RSA',//签名类型,默认RSA
  63. 'store_id' => '000000',//门店号 没有就填默认值000000
  64. //'activity_no' => '',//[非必填]优惠活动编码
  65. //'benefit_amount' => '',//[非必填]优惠金额
  66. 'extend' => json_encode($extend),//[非必填json]扩展域 如上送,在异步通知和查询接口中将返回相同的值 //H5产品URL上需要编码 //此字段云账户产品不支持
  67. //'merch_extend_params' => '',//[非必填json]商户扩展参数 商户扩展参数,JSON格式:{mchReceiveRemark:S0划款备注}//H5产品URL上需要编码
  68. ];
  69. $paramsDataTemp = $this->getSignContent($paramsData);
  70. // step2: 生成AESKey并使用公钥加密
  71. //$AESKey = $this->aes_generate(16);
  72. $pubKey = $this->loadX509Cert($this->sandpay_public);//公钥
  73. $priKey = $this->loadPk12Cert($this->business_private, $this->privateKeyPwd);//私钥
  74. //$encryptKey = $this->RSAEncryptByPub($AESKey, $pubKey);
  75. // step3: 使用AESKey加密报文
  76. //$encryptData = $this->AESEncrypt($paramsData, $AESKey);
  77. // step4: 使用私钥签名报文
  78. $sign = $this->signNew($paramsDataTemp);
  79. // step7: 使用私钥解密AESKey
  80. //$decryptAESKey = $this->RSADecryptByPri($encryptKey, $priKey);
  81. // step8: 使用解密后的AESKey解密报文
  82. //$decryptPlainText = $this->AESDecrypt($encryptData, $decryptAESKey);
  83. // step9: 使用公钥验签报文
  84. //$this->tixian_verify($decryptPlainText, $sign, $pubKey);
  85. //不参与签名
  86. $expireTime = date('YmdHis',$time + 60 * 30);
  87. $metaOption = [['s'=>'Android','n'=> '','id'=>'','sc'=>'']];
  88. $paramsNo = [
  89. 'return_url' => '',//[不参与签名]前台跳转地址 例 http://sandcash/return 支付完成之后的重定向跳转地址,用于返回商户的APP或网页 //02020004、02010005:return_url需传空,且不参与签名,例:"return_url":""
  90. 'expire_time' => $expireTime,//[不参与签名]订单失效时间 yyyyMMddHHmmss 例20180813142415,建议设置0.5~1小时
  91. 'goods_name' => $goodsName,//[不参与签名]商品名称 不能含有特殊字符
  92. 'product_code' => '02010005',//[不参与签名]产品编码 例02010005 APP包装微信小程序02010005
  93. 'clear_cycle' => '3',//[不参与签名]例 : "clear_cycle"="3"; 3-D1;0-T1;1-T0;2-D0
  94. 'sign' => $sign,//[不参与签名]签名结果
  95. 'jump_scheme' => 'sandcash://scpay',//[不参与签名]跳转scheme 没有就填默认值sandcash://scpay //此参数是安卓支付宝SDK跳转所需参数,如自定义,需要和客户端工程配置保持一致,例:android:scheme = "aaa",android:host = "bbb",jump_scheme 需填“aaa://bbb”。
  96. 'meta_option' => json_encode($metaOption),//[不参与签名]终端/网站参数[{"s":"Android","n":"","id":"","sc":""},{"s":"IOS","n":"","id":"","sc":""}] //本参数填文档示例值就可以
  97. 'limit_pay' => '',//[不参与签名][非必填]限定支付方式 例 1 微信:传1屏蔽所有信用卡 支付宝:传1-限定不能使用贷记卡 传4-限定不能使用花呗传 5-限定不能使用贷记卡+花呗
  98. 'extend_params' => '',//[不参与签名][非必填]功能参数域 实名认证域: payerVerificationInfo extend_params={"payerVerificationInfo": {"needCheckFlag":"01",//是否实名 01-实名 02-不实名"name":"张三",//姓名"certNo":"12321321312312321",//证件号"certType":"01"//证件类型-01身份证 }}
  99. ];
  100. $paramsAll = array_merge($paramsData,$paramsNo);
  101. ksort($paramsAll);
  102. $result['data'] = json_encode($paramsAll,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
  103. //$result['data'] = str_replace('\\','',$result['data']);
  104. //echo '<pre>';var_dump($paramsAll);exit;
  105. /*$data['head'] = [
  106. 'version' => '1.0',
  107. 'method' => 'sandpay.trade.pay',
  108. 'productId' => '00002021',
  109. 'accessType' => '1',
  110. 'mid' => $this->mer_no,
  111. 'plMid' => '',
  112. 'channelType' => '08',//商户的真实应用场景,可选项包括:07-互联网 08-移动端
  113. 'reqTime' => $createTime,
  114. ];
  115. $totalAmount = $this->format_money($money);
  116. $payExtra = [
  117. 'subAppid' => 'wx64e2709ce8095ab6',
  118. 'userId' => '',//微信的 openid
  119. ];
  120. $priKey = $this->loadPk12Cert($this->business_private, $this->privateKeyPwd);
  121. $data['body'] = [
  122. 'orderCode' => 'P23082214483724021874',
  123. 'totalAmount' => $totalAmount,
  124. 'subject' => '充值测试',
  125. 'body' => '充值金币',
  126. 'activityNo' => '',
  127. 'benefitAmount' => '',
  128. 'txnTimeOut' => '',
  129. 'payMode' => 'sand_wx',
  130. 'payExtra' => json_encode($payExtra),
  131. 'clientIp' => $ip,
  132. 'notifyUrl' => 'https://zhiliao.huxiukeji.com/api/sandpay/notify',
  133. 'frontUrl' => '',
  134. //'storeId' => '',
  135. //'limitPay' => '',
  136. //'terminalId' => '',
  137. //'operatorId' => '',
  138. //'clearCycle' => '0',
  139. //'riskRateInfo' => '',
  140. //'payerVerificationInfo' => '',
  141. //'bizExtendParams' => '',
  142. //'merchExtendParams' => '',
  143. //'accsplitInfo' => '',
  144. 'extend' => '',
  145. ];
  146. // step2: 私钥签名
  147. $sign = $this->sign($data, $priKey);
  148. // step3: 拼接post数据
  149. $post = array(
  150. 'charset' => 'utf-8',
  151. 'signType' => '01',
  152. 'data' => json_encode($data),
  153. 'sign' => $sign,
  154. );
  155. $url = 'https://smp-uat01.sand.com.cn/gateway/api/order/pay';
  156. //$url = 'https://caspay.sandpay.com.cn/gateway/api/order/pay';
  157. $ret = $this->http_post_json($url,$post);
  158. $sandRes = $this->parseResult($ret);
  159. // step6: 获取credential
  160. $dataRes = json_decode($sandRes['data'], true);
  161. echo '<pre>';var_dump($dataRes);exit;*/
  162. /* //实例
  163. $dataRes = [
  164. 'head' => [
  165. 'respTime' => '20230823150801',
  166. 'respMsg' => '成功',
  167. 'version' => '1.0',
  168. 'respCode' => '000000',
  169. ],
  170. 'body' => [
  171. 'totalAmount' => "000000000010",
  172. 'clearDate' => NULL,
  173. 'credential' => '{"payMode":"wx_pub","params":"{\"payInfo\":{\"trade_no\":\"weixin://wxpay/bizpayurl?pr=xxxxxx\"}}"}',
  174. 'tradeNo' => 'P23082214483724021871',
  175. 'payTime' => NULL,
  176. 'buyerPayAmount' => NULL,
  177. 'orderCode' => 'P23082214483724021871',
  178. 'discAmount' => NULL,
  179. 'prepay_id' => NULL,
  180. ],
  181. ];*/
  182. } catch (Exception $e) {
  183. $result['status'] = 0;
  184. $result['msg'] = $e->getMessage();
  185. }
  186. return $result;
  187. }
  188. /**
  189. * 衫德微信收款
  190. * https://www.yuque.com/sd_cw/xfq1vq/ut7292
  191. * @return array
  192. */
  193. public function wechath5($params=[]){
  194. $result = [
  195. 'status' => 1,
  196. 'msg' => '',
  197. 'data' => [],
  198. ];
  199. try {
  200. //统一下单参数
  201. $time = time();
  202. $createTime = date('YmdHis',$time);
  203. /*pay_extra支付拓展域为:"resourceAppid":"小程序 AppID""resourceEnv":"云开发环境 ID"*/
  204. $payExtra = ['resourceAppid'=>'wxd710096818e8f24a','resourceEnv'=>'wxd710096818e8f24a-5dvn9bbdc0aa5'];
  205. $ip = request()->ip();
  206. $ipStr = str_replace('.','_',$ip);
  207. $orderNo = isset($params['order_no']) ? $params['order_no'] : 'P23091214483724021876';
  208. $goodsName = isset($params['goods_name']) ? $params['goods_name'] : 'test';
  209. $money = isset($params['money']) ? $params['money'] : "0.1";
  210. $type = isset($params['type']) ? $params['type'] : 'gold';//vip=会员,gold=充值
  211. $extend = json_encode(['type' => $type]);
  212. $payExtraStr = json_encode($payExtra);
  213. $url = 'https://sandcash.mixienet.com.cn/pay/h5/applet?';//正式环境
  214. //$url = 'https://sandcash-uat01.sand.com.cn/pay/h5/applet?';//测试环境
  215. $dumain = $_SERVER['HTTP_HOST'];
  216. $notifyUrl = 'https://'.$dumain.'/api/sandpay/notify';
  217. $paramsData = [
  218. 'version' => '10',//版本号 默认为10
  219. 'mer_no' => $this->mer_no,//商户号 商户号是68888开头13位
  220. 'mer_order_no' => $orderNo,//商户订单号 自定义,最小长度12位 到30位
  221. 'create_time' => $createTime,//订单创建时间 yyyyMMddHHmmss例 20180813142345
  222. 'order_amt' => $money,//订单金额 例:"order_amt"="0.11" 单位: 元 //部分产品金额有最小额度限制,建议设置大于0.1元
  223. 'notify_url' => $notifyUrl,//异步通知地址 例http://sandcash/notify 主动通知商户充转提付、开户签约结果的https路径。通知地址必须为直接可以访问的URL。该地址需向杉德报备。回调地址规则:https://cshall.sandpay.com.cn/knowledge/detail/10390
  224. //'return_url' => '',
  225. 'create_ip' => $ipStr,//客户端IP 用户所在客户端的真实ip其中的“.”替换为“_” 。例192_168_0_1。
  226. 'pay_extra' => $payExtraStr,//支付扩展域
  227. 'accsplit_flag' => 'NO',//分账标识 例:NO 无分账:NO;有分账:YES
  228. 'sign_type' => 'RSA',//签名类型,默认RSA
  229. 'store_id' => '000000',//门店号 没有就填默认值000000
  230. //'activity_no' => '',//[非必填]优惠活动编码
  231. //'benefit_amount' => '',//[非必填]优惠金额
  232. 'extend' => $extend,//[非必填json]扩展域 如上送,在异步通知和查询接口中将返回相同的值 //H5产品URL上需要编码 //此字段云账户产品不支持
  233. //'merch_extend_params' => '',//[非必填json]商户扩展参数 商户扩展参数,JSON格式:{mchReceiveRemark:S0划款备注}//H5产品URL上需要编码
  234. ];
  235. $paramsDataTemp = $this->getSignContent($paramsData);
  236. $sign = $this->signNew($paramsDataTemp);
  237. //不参与签名
  238. $expireTime = date('YmdHis',$time + 60 * 30);
  239. $metaOption = json_encode([['s'=>'Android','n'=> '','id'=>'','sc'=>''],['s'=>'IOS','n'=> '','id'=>'','sc'=>'']]);
  240. $paramsNo = [
  241. //[不参与签名]前台跳转地址 例 http://sandcash/return 支付完成之后的重定向跳转地址,用于返回商户的APP或网页 //02020004、02010005:return_url需传空,且不参与签名,例:"return_url":""
  242. 'expire_time' => $expireTime,//[不参与签名]订单失效时间 yyyyMMddHHmmss 例20180813142415,建议设置0.5~1小时
  243. 'goods_name' => $goodsName,//[不参与签名]商品名称 不能含有特殊字符
  244. 'product_code' => '02010006',//[不参与签名]产品编码 例02010005 APP包装微信小程序02010005
  245. 'clear_cycle' => '3',//[不参与签名]例 : "clear_cycle"="3"; 3-D1;0-T1;1-T0;2-D0
  246. 'sign' => $sign,//[不参与签名]签名结果
  247. 'jump_scheme' => 'sandcash://scpay',//[不参与签名]跳转scheme 没有就填默认值sandcash://scpay //此参数是安卓支付宝SDK跳转所需参数,如自定义,需要和客户端工程配置保持一致,例:android:scheme = "aaa",android:host = "bbb",jump_scheme 需填“aaa://bbb”。
  248. 'meta_option' => $metaOption,//[不参与签名]终端/网站参数[{"s":"Android","n":"","id":"","sc":""},{"s":"IOS","n":"","id":"","sc":""}] //本参数填文档示例值就可以
  249. 'limit_pay' => '',//[不参与签名][非必填]限定支付方式 例 1 微信:传1屏蔽所有信用卡 支付宝:传1-限定不能使用贷记卡 传4-限定不能使用花呗传 5-限定不能使用贷记卡+花呗
  250. 'extend_params' => '',//[不参与签名][非必填]功能参数域 实名认证域: payerVerificationInfo extend_params={"payerVerificationInfo": {"needCheckFlag":"01",//是否实名 01-实名 02-不实名"name":"张三",//姓名"certNo":"12321321312312321",//证件号"certType":"01"//证件类型-01身份证 }}
  251. ];
  252. $paramsAll = array_merge($paramsData,$paramsNo);
  253. if (!empty($paramsAll)) {
  254. //UrlEncode编码字段:goods_name , notify_url,return_url,pay_extra,meta_option,extend,merch_extend_params,sign
  255. foreach ($paramsAll as $key => &$value) {
  256. if (in_array($key,['goods_name','notify_url','return_url','pay_extra','meta_option','extend','merch_extend_params','sign'])) {
  257. $value = urlencode($value);
  258. }
  259. }
  260. }
  261. $urlStr = $url.$this->getSignContent($paramsAll);
  262. $result['data'] = $urlStr;
  263. } catch (Exception $e) {
  264. $result['status'] = 0;
  265. $result['msg'] = $e->getMessage();
  266. }
  267. return $result;
  268. }
  269. //实时付款接口
  270. //https://open.sandpay.com.cn/product/detail/43327/43930/
  271. public function payout($order_no,$money,$remark,$bank,$realname){
  272. $money = $this->format_money($money);
  273. $data = [
  274. 'version' => 10, //默认值10
  275. 'productId' => '00000004', //付款对私:00000004 付款对公:00000003
  276. 'tranTime' => date('YmdHis', time()),
  277. 'orderCode' => $order_no,
  278. 'tranAmt' => $money,
  279. 'currencyCode' => '156',
  280. 'accAttr' => '0',//账户属性-0对私(默认)
  281. 'accType' => '4', //2-存折 3-公司账户 4-银行卡 注:accAttr选择对公时,accType选公司账户
  282. 'accNo' => $bank,
  283. 'accName' => $realname,
  284. 'remark' => $remark,
  285. 'extend' => '',
  286. ];
  287. $config = [
  288. 'publicKeyPath' => $this->sandpay_public, // 公钥文件
  289. 'privateKeyPath' => $this->business_private, // 私钥文件
  290. 'privateKeyPwd' => $this->privateKeyPwd, // 私钥证书密码
  291. 'apiUrl' => 'https://caspay.sandpay.com.cn/agent-main/openapi/agentpay', // 接口地址
  292. 'variable' =>[
  293. 'transCode' => 'RTPM', //交易码 https://open.sandpay.com.cn/product/detail/43996//
  294. 'accessType' => '0', // 接入类型 0-商户接入,默认 1-平台接入
  295. 'merId' => $this->mer_no,
  296. ],
  297. ];
  298. $post = $config['variable'];
  299. //
  300. $datalist = [];
  301. $datalist['body'] = $data;
  302. // step2: 生成AESKey并使用公钥加密
  303. $AESKey = $this->aes_generate(16);
  304. $pubKey = $this->loadX509Cert($config['publicKeyPath']);
  305. $priKey = $this->loadPk12Cert($config['privateKeyPath'], $config['privateKeyPwd']);
  306. $encryptKey = $this->RSAEncryptByPub($AESKey, $pubKey);
  307. // step3: 使用AESKey加密报文
  308. $encryptData = $this->AESEncrypt($datalist['body'], $AESKey);
  309. // step4: 使用私钥签名报文
  310. $sign = $this->sign($datalist['body'], $priKey);
  311. // step5: 拼接post数据
  312. $post['sign'] = $sign;
  313. $post['encryptKey'] = $encryptKey;
  314. $post['encryptData'] = $encryptData;
  315. $datalist['head'] = $post;
  316. $url = $config['apiUrl'];
  317. $ret = $this->http_post_json($url,$post);
  318. parse_str($ret, $arr);
  319. try {
  320. // step7: 使用私钥解密AESKey
  321. $decryptAESKey = $this->RSADecryptByPri($arr['encryptKey'], $priKey);
  322. // step8: 使用解密后的AESKey解密报文
  323. $decryptPlainText = $this->AESDecrypt($arr['encryptData'], $decryptAESKey);
  324. // step9: 使用公钥验签报文
  325. $this->tixian_verify($decryptPlainText, $arr['sign'], $pubKey);
  326. //返回结果
  327. //0000 成功
  328. //0001 银行处理中 请等待银行返回的明确结果 . 需要查询
  329. //0002 银行返回超时 请等待银行返回的明确结果 . 需要查询
  330. //其他 需要重新发起
  331. $decryptPlainText = json_decode($decryptPlainText,true);
  332. if(is_array($decryptPlainText) && isset($decryptPlainText['respCode']) && $decryptPlainText['respCode'] == '0000'){
  333. return true;
  334. }
  335. if(is_array($decryptPlainText) && isset($decryptPlainText['respCode']) && in_array($decryptPlainText['respCode'],['0001','0002'])){
  336. return 'wait';
  337. }
  338. if(is_array($decryptPlainText) && isset($decryptPlainText['respCode']) && isset($decryptPlainText['respDesc'])){
  339. return $decryptPlainText['respCode'].':'.$decryptPlainText['respDesc'];
  340. }
  341. return '提现失败';
  342. /*return json_encode([
  343. 'verify' => $decryptPlainText,
  344. 'jjson' => json_encode($datalist['body']),
  345. 'vjson' => json_encode($datalist['head']),
  346. 'json' => json_encode($arr),
  347. 'url' => 'https://open.sandpay.com.cn/product/detail/43324/43895/',
  348. ]);*/
  349. } catch (\Exception $e) {
  350. return json_encode([
  351. 'json' => $e->getMessage(),
  352. 'url' => 'https://open.sandpay.com.cn/product/detail/43324/43895/',
  353. ]);
  354. echo $e->getMessage();
  355. exit;
  356. }
  357. }
  358. //拉起一键快捷
  359. public function onekey_dopayment($order_no,$money,$remark,$user_id){
  360. $data = [
  361. 'version' => 10, //默认值10
  362. 'mer_no' => $this->mer_no, //商户号 深圳市恩特宏云科技有限公司
  363. 'mer_key' => $this->mer_key, // 商户私钥通过安卓APK工具解析出来的KEY1
  364. 'mer_order_no' => $order_no, //商户唯一订单号
  365. 'create_time' => date('YmdHis'),
  366. 'expire_time' => date('YmdHis', time()+30*60), //订单失效时间
  367. 'order_amt' => ''.$money.'', //订单支付金额 单位元,最低 0.11元
  368. 'notify_url' => $this->domain_url.'/api/sandpay/notify', //订单支付异步通知
  369. 'return_url' => $this->h5_url . '/#/pages/public/paySuc?type=recharge',//支付成功后自动跳转url
  370. 'create_ip' => str_replace('.','_',request()->ip()),
  371. 'goods_name' => $remark,
  372. 'store_id' => '000000', //没有就填默认值000000
  373. 'product_code' => '05030001', // 产品编码: 云函数h5:02010006;支付宝H5:02020002;微信公众号H5:02010002;
  374. //一键快捷:05030001;H5快捷:06030001;支付宝扫码:02020005 ;快捷充值: 06030003;
  375. //电子钱包【云账户】:开通账户并支付product_code应为:04010001;消费(C2C)product_code 为:04010003 ; 我的账户页面 product_code 为:00000001
  376. 'clear_cycle' => '3', //
  377. 'pay_extra' => json_encode(['userId'=>'test'.$user_id]),//resourceAppid:小程序 AppID ;resourceEnv:云开发环境 ID,云函数所需参数,如不清楚请商户群里详问杉德联调人员
  378. 'accsplit_flag' => 'NO', //无分账:NO;有分账:YES
  379. 'jump_scheme' => 'sandcash://scpay',
  380. // 'meta_option' => json_encode([["s" => "Android","n" => "wxDemo","id" => "com.pay.paytypetest","sc" => "com.pay.paytypetest"]]),
  381. 'meta_option' => json_encode([['s'=>'Android','n'=>'','id'=>'','sc'=>''],['s'=>'IOS','n'=>'','id'=>'','sc'=>'']]),
  382. 'sign_type' => 'MD5'
  383. ];
  384. $temp = $data;
  385. unset($temp['goods_name']);
  386. unset($temp['jump_scheme']);
  387. unset($temp['expire_time']);
  388. unset($temp['product_code']);
  389. unset($temp['clear_cycle']);
  390. unset($temp['meta_option']);
  391. // file_put_contents('log.txt', date('Y-m-d H:i:s', time()) . ' 签名串:' . $this->getSignContent($temp)."&key=OfVZ1x+EcLjZYClVhkD9gqlWnO3" . "\r\n", FILE_APPEND); // key对应商户私钥通过安卓APK工具解析出来的MD5KEY
  392. $sign = strtoupper(md5($this->getSignContent($temp).'&key='.$this->md5key)); // key对应商户私钥通过安卓APK工具解析出来的MD5KEY
  393. $data['sign'] = $sign;
  394. $query = http_build_query($data);
  395. $payurl = "https://sandcash.mixienet.com.cn/pay/h5/fastpayment?".$query; // 云函数h5:applet;支付宝H5:alipay;微信公众号H5:wechatpay; //一键快捷:fastpayment;H5快捷:unionpayh5;支付宝扫码:alipaycode ;快捷充值:quicktopup ;电子钱包【云账户】:cloud
  396. return $payurl; // 返回支付url
  397. }
  398. //拉起h5快捷
  399. public function dopayment($order_no,$money,$remark,$banknum = ''){
  400. $data = [
  401. 'version' => 10, //默认值10
  402. 'mer_no' => $this->mer_no, //商户号 深圳市恩特宏云科技有限公司
  403. 'mer_key' => $this->mer_key, // 商户私钥通过安卓APK工具解析出来的KEY1
  404. 'mer_order_no' => $order_no, //商户唯一订单号
  405. 'create_time' => date('YmdHis'),
  406. 'expire_time' => date('YmdHis', time()+30*60), //订单失效时间
  407. 'order_amt' => ''.$money.'', //订单支付金额 单位元,最低 0.11元
  408. 'notify_url' => $this->domain_url.'/api/sandpay/notify', //订单支付异步通知
  409. 'return_url' => $this->domain_url.'/index/sandpay/payreturn', //订单前端页面跳转地址
  410. 'create_ip' => str_replace('.','_',request()->ip()),
  411. 'goods_name' => $remark,
  412. 'store_id' => '000000', //没有就填默认值000000
  413. 'product_code' => '06030001', // 产品编码: 云函数h5:02010006;支付宝H5:02020002;微信公众号H5:02010002;
  414. //一键快捷:05030001;H5快捷:06030001;支付宝扫码:02020005 ;快捷充值: 06030003;
  415. //电子钱包【云账户】:开通账户并支付product_code应为:04010001;消费(C2C)product_code 为:04010003 ; 我的账户页面 product_code 为:00000001
  416. 'clear_cycle' => '3', //
  417. 'pay_extra' => json_encode(['cardNo'=>$banknum]),//resourceAppid:小程序 AppID ;resourceEnv:云开发环境 ID,云函数所需参数,如不清楚请商户群里详问杉德联调人员
  418. 'accsplit_flag' => 'NO', //无分账:NO;有分账:YES
  419. 'jump_scheme' => 'sandcash://scpay',
  420. // 'meta_option' => json_encode([["s" => "Android","n" => "wxDemo","id" => "com.pay.paytypetest","sc" => "com.pay.paytypetest"]]),
  421. 'meta_option' => json_encode([['s'=>'Android','n'=>'','id'=>'','sc'=>''],['s'=>'IOS','n'=>'','id'=>'','sc'=>'']]),
  422. 'sign_type' => 'MD5'
  423. ];
  424. if(!$banknum){
  425. unset($data['pay_extra']);
  426. }
  427. $temp = $data;
  428. unset($temp['goods_name']);
  429. unset($temp['jump_scheme']);
  430. unset($temp['expire_time']);
  431. unset($temp['product_code']);
  432. unset($temp['clear_cycle']);
  433. unset($temp['meta_option']);
  434. // file_put_contents('log.txt', date('Y-m-d H:i:s', time()) . ' 签名串:' . $this->getSignContent($temp)."&key=OfVZ1x+EcLjZYClVhkD9gqlWnO3" . "\r\n", FILE_APPEND); // key对应商户私钥通过安卓APK工具解析出来的MD5KEY
  435. $sign = strtoupper(md5($this->getSignContent($temp).'&key='.$this->md5key)); // key对应商户私钥通过安卓APK工具解析出来的MD5KEY
  436. $data['sign'] = $sign;
  437. $query = http_build_query($data);
  438. $payurl = "https://sandcash.mixienet.com.cn/pay/h5/unionpayh5?".$query; // 云函数h5:applet;支付宝H5:alipay;微信公众号H5:wechatpay; //一键快捷:fastpayment;H5快捷:unionpayh5;支付宝扫码:alipaycode ;快捷充值:quicktopup ;电子钱包【云账户】:cloud
  439. return $payurl; // 返回支付url
  440. }
  441. private function getSignContent($params) {
  442. ksort($params);
  443. $stringToBeSigned = "";
  444. $i = 0;
  445. foreach ($params as $k => $v) {
  446. if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
  447. if ($i == 0) {
  448. $stringToBeSigned .= "$k" . "=" . "$v";
  449. } else {
  450. $stringToBeSigned .= "&" . "$k" . "=" . "$v";
  451. }
  452. $i++;
  453. }
  454. }
  455. unset ($k, $v);
  456. return $stringToBeSigned;
  457. }
  458. private function getSignContentNew($params) {
  459. ksort($params);
  460. $stringToBeSigned = "";
  461. $i = 0;
  462. foreach ($params as $k => $v) {
  463. if ("@" != substr($v, 0, 1)) {
  464. if ($i == 0) {
  465. $stringToBeSigned .= "$k" . "=" . "$v";
  466. } else {
  467. $stringToBeSigned .= "&" . "$k" . "=" . "$v";
  468. }
  469. $i++;
  470. }
  471. }
  472. unset ($k, $v);
  473. return $stringToBeSigned;
  474. }
  475. private function checkEmpty($value)
  476. {
  477. if (!isset($value))
  478. return true;
  479. if ($value === null)
  480. return true;
  481. if (trim($value) === "")
  482. return true;
  483. return false;
  484. }
  485. //校验
  486. public function verify($plainText, $sign)
  487. {
  488. $publickey = $this->publicKey();
  489. if($publickey === false){
  490. return 0;
  491. }
  492. $resource = openssl_pkey_get_public($publickey);
  493. $result = openssl_verify($plainText, base64_decode($sign), $resource);
  494. openssl_free_key($resource);
  495. return $result;
  496. }
  497. public function publicKey()
  498. {
  499. try {
  500. $file = file_get_contents($this->sandpublic);
  501. if (!$file) {
  502. throw new \Exception('getPublicKey::file_get_contents ERROR');
  503. }
  504. $cert = chunk_split(base64_encode($file), 64, "\n");
  505. $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
  506. $res = openssl_pkey_get_public($cert);
  507. $detail = openssl_pkey_get_details($res);
  508. openssl_free_key($res);
  509. if (!$detail) {
  510. throw new \Exception('getPublicKey::openssl_pkey_get_details ERROR');
  511. }
  512. return $detail['key'];
  513. } catch (\Exception $e) {
  514. throw $e;
  515. }
  516. }
  517. //--------------------------------------------end基础参数配置------------------------------------------------
  518. /**
  519. * 获取公钥
  520. * @param $path
  521. * @return mixed
  522. * @throws Exception
  523. */
  524. function loadX509Cert($path)
  525. {
  526. try {
  527. $file = file_get_contents($path);
  528. if (!$file) {
  529. throw new \Exception('loadx509Cert::file_get_contents ERROR');
  530. }
  531. $cert = chunk_split(base64_encode($file), 64, "\n");
  532. $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
  533. $res = openssl_pkey_get_public($cert);
  534. $detail = openssl_pkey_get_details($res);
  535. openssl_free_key($res);
  536. if (!$detail) {
  537. throw new \Exception('loadX509Cert::openssl_pkey_get_details ERROR');
  538. }
  539. return $detail['key'];
  540. } catch (\Exception $e) {
  541. throw $e;
  542. }
  543. }
  544. /**
  545. * 获取私钥
  546. * @param $path
  547. * @param $pwd
  548. * @return mixed
  549. * @throws Exception
  550. */
  551. function loadPk12Cert($path, $pwd)
  552. {
  553. try {
  554. $file = file_get_contents($path);
  555. if (!$file) {
  556. throw new \Exception('loadPk12Cert::file
  557. _get_contents');
  558. }
  559. if (!openssl_pkcs12_read($file, $cert, $pwd)) {
  560. throw new \Exception('loadPk12Cert::openssl_pkcs12_read ERROR');
  561. }
  562. return $cert['pkey'];
  563. } catch (\Exception $e) {
  564. throw $e;
  565. }
  566. }
  567. /**
  568. * 私钥签名
  569. * @param $plainText
  570. * @param $path
  571. * @return string
  572. * @throws Exception
  573. */
  574. function sign($plainText, $path)
  575. {
  576. $plainText = json_encode($plainText);
  577. try {
  578. $resource = openssl_pkey_get_private($path);
  579. $result = openssl_sign($plainText, $sign, $resource);
  580. openssl_free_key($resource);
  581. if (!$result) {
  582. throw new \Exception('签名出错' . $plainText);
  583. }
  584. return base64_encode($sign);
  585. } catch (\Exception $e) {
  586. throw $e;
  587. }
  588. }
  589. function signNew($str) {
  590. $file = file_get_contents($this->business_private);
  591. if (!$file) {
  592. throw new \Exception('loadPk12Cert::file
  593. _get_contents');
  594. }
  595. if (!openssl_pkcs12_read($file, $cert, $this->privateKeyPwd)) {
  596. throw new \Exception('loadPk12Cert::openssl_pkcs12_read ERROR');
  597. }
  598. $pem = $cert['pkey'];
  599. openssl_sign($str, $sign, $pem);
  600. $sign = base64_encode($sign);
  601. return $sign;
  602. }
  603. /**
  604. * 公钥验签
  605. * @param $plainText
  606. * @param $sign
  607. * @param $path
  608. * @return int
  609. * @throws Exception
  610. */
  611. function tixian_verify($plainText, $sign, $path)
  612. {
  613. $resource = openssl_pkey_get_public($path);
  614. $result = openssl_verify($plainText, base64_decode($sign), $resource);
  615. openssl_free_key($resource);
  616. if (!$result) {
  617. throw new \Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign, '02002');
  618. }
  619. return $result;
  620. }
  621. /**
  622. * 公钥加密AESKey
  623. * @param $plainText
  624. * @param $puk
  625. * @return string
  626. * @throws Exception
  627. */
  628. function RSAEncryptByPub($plainText, $puk)
  629. {
  630. if (!openssl_public_encrypt($plainText, $cipherText, $puk, OPENSSL_PKCS1_PADDING)) {
  631. throw new \Exception('AESKey 加密错误');
  632. }
  633. return base64_encode($cipherText);
  634. }
  635. /**
  636. * 私钥解密AESKey
  637. * @param $cipherText
  638. * @param $prk
  639. * @return string
  640. * @throws Exception
  641. */
  642. function RSADecryptByPri($cipherText, $prk)
  643. {
  644. if (!openssl_private_decrypt(base64_decode($cipherText), $plainText, $prk, OPENSSL_PKCS1_PADDING)) {
  645. throw new \Exception('AESKey 解密错误');
  646. }
  647. return (string)$plainText;
  648. }
  649. /**
  650. * AES加密
  651. * @param $plainText
  652. * @param $key
  653. * @return string
  654. * @throws \Exception
  655. */
  656. function AESEncrypt($plainText, $key)
  657. {
  658. $plainText = json_encode($plainText);
  659. $result = openssl_encrypt($plainText, 'AES-128-ECB', $key, 1);
  660. if (!$result) {
  661. throw new \Exception('报文加密错误');
  662. }
  663. return base64_encode($result);
  664. }
  665. /**
  666. * AES解密
  667. * @param $cipherText
  668. * @param $key
  669. * @return string
  670. * @throws \Exception
  671. */
  672. function AESDecrypt($cipherText, $key)
  673. {
  674. $result = openssl_decrypt(base64_decode($cipherText), 'AES-128-ECB', $key, 1);
  675. if (!$result) {
  676. throw new \Exception('报文解密错误', 2003);
  677. }
  678. return $result;
  679. }
  680. /**
  681. * 生成AESKey
  682. * @param $size
  683. * @return string
  684. */
  685. function aes_generate($size)
  686. {
  687. $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  688. $arr = array();
  689. for ($i = 0; $i < $size; $i++) {
  690. $arr[] = $str[mt_rand(0, 61)];
  691. }
  692. return implode('', $arr);
  693. }
  694. /**
  695. * 发送请求
  696. * @param $url
  697. * @param $param
  698. * @return bool|mixed
  699. * @throws Exception
  700. */
  701. function http_post_json($url, $param)
  702. {
  703. if (empty($url) || empty($param)) {
  704. return false;
  705. }
  706. $param = http_build_query($param);
  707. try {
  708. $ch = curl_init();//初始化curl
  709. curl_setopt($ch, CURLOPT_URL, $url);
  710. curl_setopt($ch, CURLOPT_POST, 1);
  711. curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
  712. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  713. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  714. //正式环境时解开注释
  715. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  716. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  717. $data = curl_exec($ch);//运行curl
  718. curl_close($ch);
  719. if (!$data) {
  720. throw new \Exception('请求出错');
  721. }
  722. return $data;
  723. } catch (\Exception $e) {
  724. throw $e;
  725. }
  726. }
  727. public function format_money($money){
  728. // $money = 345678.10;
  729. $money = $money * 100;
  730. $zero_arr = [
  731. 0 => '000000000000',
  732. 1 => '00000000000',
  733. 2 => '0000000000',
  734. 3 => '000000000',
  735. 4 => '00000000',
  736. 5 => '0000000',
  737. 6 => '000000',
  738. 7 => '00000',
  739. 8 => '0000',
  740. 9 => '000',
  741. 10 => '00',
  742. 11 => '0',
  743. 12 => '',
  744. ];
  745. $newmoney = $zero_arr[strlen($money)] . $money;
  746. return $newmoney;
  747. // dump($newmoney);
  748. }
  749. // curl.解析返回数据
  750. protected function parseResult($result)
  751. {
  752. $arr = array();
  753. $response = urldecode($result);
  754. $arrStr = explode('&', $response);
  755. foreach ($arrStr as $str) {
  756. $p = strpos($str, "=");
  757. $key = substr($str, 0, $p);
  758. $value = substr($str, $p + 1);
  759. $arr[$key] = $value;
  760. }
  761. return $arr;
  762. }
  763. }