ShouxinyiApiRequest.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. <?php
  2. namespace app\traits;
  3. use app\model\sxy\DeclareLog;
  4. use crmeb\exceptions\AuthException;
  5. use phpseclib3\Net\SFTP;
  6. use app\controller\Aes;
  7. use phpseclib3\Crypt\RSA;
  8. use think\Exception;
  9. use think\facade\App;
  10. use think\facade\Db;
  11. /**
  12. * 首信易统一请求API
  13. * Trait ShouxinyiApiRequest
  14. * @package app\traits
  15. */
  16. trait ShouxinyiApiRequest
  17. {
  18. /**
  19. * 申请商户
  20. * @return bool|string
  21. * @author lin
  22. * @created 2021/5/13 9:15
  23. * */
  24. public static function merchantApply($merchant=[]){
  25. try {
  26. $url = env('SHOUXINYI.HOSTNAME') . 'serviceprovider/declaration/declare';
  27. $data = [];
  28. $data['merchantId'] = env('SHOUXINYI.MERCHANTID'); // 商户编号
  29. $data['requestId'] = self::getOrderNumber('sxy'); //申请商户订单号
  30. $data['operationType'] = $merchant['operationType'] ?? 'CREATE';//CREATE 创建 MODIFY 更新
  31. $data['notifyUrl'] = env('APP_URL') . '/api/notify/createMerchant_5upay';//回调地址
  32. $data['extendedParameters'] = 'autoReview:FALSE,sendActiveEmail:FALSE';//autoReview 是否跳过流程 TRUE 是, FALSE 否 sendActiveEmail 是否发送激活邮件 TRUE 是, FALSE 否
  33. //CREATE 创建 MODIFY 更新
  34. // switch ($merchant['is_again']) {
  35. // case 1 || 2:
  36. // $data['operationType'] = 'MODIFY';
  37. // break;
  38. // case 0:
  39. // default:
  40. // $data['operationType'] = 'CREATE';
  41. // break;
  42. // }
  43. $data['operationType'] = 'CREATE';
  44. /**
  45. * 商户签约性质:registerRole
  46. * INDIVIDUAL_BUSINESS 个体工商户
  47. * PUBLIC_INSTITUTIONS 事业单位
  48. * PRIVATE_NON_ENTERPRISE_ORGANIZATIONS 民办非企业组织
  49. * SOCIAL_ORGANIZATION 社会团体
  50. * UNINCORPORATED_ENTERPRISE 非法人企业
  51. * ENTERPRISE_LEGAL_PERSON 企业法人
  52. * NATURAL_PERSON 自然人
  53. * GOVERNMENT_AGENCIES 政府机关
  54. * OTHERS 其他
  55. * */
  56. /**
  57. * 证书类型:cerType
  58. * UNIFORM_SOCIAL_CREDIT_CODE 统一社会信用代码证
  59. * PRIVATE_NON_ENTERPRISE_UNIT_REGISTRATION_CERTIFICATE 民办非企业单位登记证书
  60. * INDIVIDUAL_BUSINESS_LICENCE 个体工商户营业执照
  61. * CERTIFICATE_OF_REGISTRATION_OF_SOCIAL_GROUP_LEGAL_PERSON 社会团体法人登记证书
  62. * LEGAL_PERSON_CERTIFICATE_OF_INSTITUTION 事业单位法人证书
  63. * FUND_LEGAL_PERSON_REGISTRATION_CERTIFICATE 基金法人登记证书
  64. * ID_CARD 身份证
  65. * BUSINESS_LICENSE 营业执照
  66. * OTHERS 其他 (当签约性质不为自然人时必传)
  67. */
  68. $data['baseInfo'] = [//商户基本信息
  69. 'signedType' => $merchant['signedType'] ?? 'BY_SPLIT_BILL',//BY_SPLIT_BILL 被分账方 GENERAL_SUB_MERCHANT 收单子商户
  70. 'signedName' => $merchant['signedName'],//签约名称
  71. 'registerRole' => $merchant['registerRole'],//商户签约性质
  72. 'cerType' => $merchant['cerType'],//证书类型
  73. 'signedShorthand' => $merchant['signedShorthand'],//商户简称
  74. 'businessAddressProvince' => $merchant['businessAddressProvince'],//省
  75. 'businessAddressCity' => $merchant['businessAddressCity'],//市
  76. 'businessAddressArea' => $merchant['businessAddressArea'],//区
  77. 'businessAddress' => $merchant['businessAddress'],//详细地址
  78. 'contactName' => $merchant['contactName'],//联系人姓名
  79. 'contactEmail' => $merchant['contactEmail'],//联系人邮箱
  80. 'contactPhone' => $merchant['contactPhone'],//联系人电话
  81. 'businessClassification' => $merchant['businessClassification'],//业务分类
  82. 'desireAuth' => $merchant['desireAuth']??'',//开户意愿核实类型
  83. ];
  84. $data['bankCardInfo'] = [//结算信息
  85. 'bankCode' => $merchant['bankCode'],//开户行
  86. 'bankBranchName' => $merchant['bankBranchName'],//支行名称(结算银行卡属性为对公时必填)
  87. 'accountName' => $merchant['accountName'],//开户名称
  88. 'bankCardNo' => $merchant['bankCardNo'],//开户账号
  89. 'cityCode' => $merchant['cityCode'],//市编码
  90. 'provinceCode' => $merchant['provinceCode'],//省编码
  91. 'accountType' => $merchant['accountType'],//结算银行卡属性PUBLIC 对公 PRIVATE 对私 (商户类型为个体工商户时必填,个人默认对私,企业等默认对公)
  92. 'liquidationType' => $merchant['liquidationType'] ?? 'WITHDRAW',//SETTLE 结算 WITHDRAW 提现 (不传默认结算)
  93. 'withdrawRateType' => $merchant['withdrawRateType'] ?? 'SINGLE',//SINGLE 单笔 RATE 百分比 (清算方式选择提现时必传)
  94. 'withdrawRate' => $merchant['withdrawRate'] ?? '1',//(清算方式选择提现时必传)如传小数则限制两位小数,配置此项时校验服务商提现费率,如果服务商是百分比费率配置则此项也必须配置百分比,固定值同样,且此项配置费率不能小于服务商费率
  95. ];
  96. $data['desireAuthInfo'] = [//开户意愿核实信息(开户意愿核实类型选择手机短信验证时此项必传)
  97. 'legalPersonName' => $merchant['legalPersonName'],//法人姓名
  98. 'legalPersonIdNo' => $merchant['legalPersonIdNo'],//法人身份证号
  99. 'legalPersonPhoneNo' => $merchant['legalPersonPhoneNo'],//法人手机号
  100. ];
  101. /**
  102. * 法人证件类型:legalPersonIdType
  103. * IDCARD 身份证
  104. * PASSPORT 护照
  105. * OFFICERPASS 军官证
  106. * ARMEDPOLICE 武警证
  107. * HM_VISITORPASS 港澳居民往来内地通行证
  108. * RESIDENCEBOOKLET 户口簿
  109. * T_VISITORPASS 台湾居民来往大陆通行证
  110. * FOREIGNESTAYFOREVER 外国人永久居留证
  111. * OTHER 其它证件号
  112. */
  113. $data['certificateInfo'] = [//商户资质信息
  114. 'legalPersonName' => $merchant['legalPersonName'],//法人姓名
  115. 'profession' => $merchant['profession'],//法人职业
  116. 'legalPersonIdType' => $merchant['legalPersonIdType'],//法人证件类型
  117. 'legalPersonIdNo' => $merchant['legalPersonIdNoSup'],//证件号码
  118. 'idEffectiveDateStart' => $merchant['idEffectiveDateStart'],//法人证件有效期开始日期
  119. 'idEffectiveDateEnd' => $merchant['idEffectiveDateEnd'],//法人证件有效期结束日期
  120. 'cerNoType' => $merchant['cerNoType'],//企业证件类型:SOCIAL_CREDIT_NO 统一社会信用代码 BUSINESS_LICENSE_NO 营业执照号码 (商户类型不为自然人时必传)
  121. 'cerNo' => $merchant['cerNo'],//资质证书编号
  122. 'sellingArea' => $merchant['sellingArea'],//营业面积:A 500平米以上 B 200-500平米(含) C 200平米以下 (商户类型不为自然人时必传)
  123. 'staffSize' => $merchant['staffSize'],//公司员工规模A 100人以上 B 50-100人(含) C 50人以下 (商户类型不为自然人时必传)
  124. 'tradingScenarios' => $merchant['tradingScenarios'] ?? '',//交易场景说明WEBSITE 网站 APP APP WECHAT_APPLET 公众号/小程序/生活号 (可多选,多选时参数用英文半角逗号隔开,商户类型不为自然人或被分账方时必传)
  125. 'webSite' => $merchant['webSite'] ?? '',//网站网址 (商户类型不为自然人或被分账方时,如交易场景基于PC网站时必传)
  126. 'webSiteName' => $merchant['webSiteName'] ?? '',//网站名称 (商户类型不为自然人或被分账方时,如交易场景基于PC网站时必传)
  127. 'icp' => $merchant['icp'] ?? '',//ICP备案号 (商户类型不为自然人或被分账方时,如交易场景基于PC网站时必传)
  128. 'appName' => $merchant['appName'] ?? '',//App名称 (商户类型不为自然人或被分账方时,如交易场景基于APP时必传)
  129. 'wechatAppletName' => $merchant['wechatAppletName'] ?? '',//公众号/小程序/生活号 (商户类型不为自然人或被分账方时,如交易场景基于公众号/小程序/生活号时必传)
  130. 'testAccountInfo' => $merchant['testAccountInfo'] ?? '',//填写可用于首信易审核人员测试交易全流程的测试账号密码
  131. 'businessLicensePath' => $merchant['businessLicensePath'] ?? '',//营业执照照片路径 需上传到sftp(商户类型不为自然人时必传,最大不超过50M,格式限制为jpg,jpeg,png,pdf)
  132. 'openAccountPath' => $merchant['openAccountPath'] ?? '',//开户许可证照片路径 需上传到sftp(商户类型不为自然人及个体工商户时必传,为个体工商户时此项和legalPersonBankCardPath二选一进行传递,最大不超过50M,格式限制为jpg,jpeg,png,pdf)
  133. 'legalIdCardProsPath' => $merchant['legalIdCardProsPath'] ?? '',//法人证件人像面路径
  134. 'legalIdCardConsPath' => $merchant['legalIdCardConsPath'] ?? '',//法人证件国徽面路径
  135. 'desireAuthPath' => $merchant['desireAuthPath'] ?? '',//确认入网意愿视频路径 认证类型选择视频认证时必填
  136. 'holdingIdCardPath' => $merchant['holdingIdCardPath'] ?? '',//法人手持证件影印件路径 认证类型选择手机验证时必传
  137. 'webSitePath' => $merchant['webSitePath'] ?? '',//商户网址截图影印件路径 商户类型不为自然人或被分账方时,如交易场景基于PC网站时必传
  138. 'wechatAppletPath' => $merchant['wechatAppletPath'] ?? '',//公众号/小程序/生活号业务流程截图影印件路径 商户类型不为自然人或被分账方时,如交易场景基于公众号/小程序/生活号时必传
  139. 'appPath' => $merchant['appPath'] ?? '',//APP首页截图影印件路径 商户类型不为自然人或被分账方时,如交易场景基于APP时必传
  140. 'legalPersonBankCardPath' => $merchant['legalPersonBankCardPath'] ?? '',//法人银行卡图影印件路径 商户类型为自然人时必传,为个体工商户时此项和openAccountPath二选一进行传递
  141. 'desireAuthOtherPath' => $merchant['desireAuthOtherPath'] ?? '',//确认入网意愿其他验证方式路径 开户意愿核实类型选择“其他验证方式”的时候必传
  142. 'otherCerPath' => $merchant['otherCerPath'] ?? '',//其他资质文件路径
  143. 'legalPersonPhone' => $merchant['legalPersonPhoneNo'],//合同类型为“电子签”时必填;必须为法人的手机号。
  144. ];
  145. $data['contractInfo'] = [
  146. 'contractType' => 'ELECTRON',//ELECTRON 电子签 PAPER 纸质合同 默认为“纸质合同”,选择“电子签”时将授权首信易通过电子签名的方式进行签约,无需邮寄合同。
  147. ];
  148. /**
  149. * 支付方式:paymentMode
  150. * B2C个人网银
  151. * B2B企业网银
  152. * EXPRESSPAY_ISNOTDIRECT_DEBIT快捷支付标准版借记卡
  153. * EXPRESS_PAY_ISNOTDIRECT_CREDIT快捷支付标准版贷记卡
  154. * EXPRESS_PAY_ISDIRECT_DEBIT快捷支付专业版借记卡
  155. * EXPRESS_PAY_ISDIRECT_CREDIT快捷支付专业版_贷记卡
  156. * ALIPAY_SCANCODE支付宝扫码
  157. * WEIXIN_OFFICIAL微信公众号
  158. * WEIXIN_MINIAPPS微信小程序
  159. * ALIPAY_WAP支付宝wap
  160. * ALIPAY_OFFICIAL支付宝生活号
  161. * WEIXIN_EQRCODE易码付-微信
  162. * ALIPAY_EQRCODE易码付-支付宝(商户入网类型=收单子商户时可传,如不传则按服务商默认费率)
  163. * */
  164. //入网记录
  165. // echo $data['requestId'].$merchant['signedName'].'<br>';
  166. $log = new DeclareLog();
  167. // $is = $log -> getValueLog("id",$merchant['m_id']);
  168. // if($is){
  169. // $log -> where('m_id',$merchant['m_id']) -> update(array(
  170. // 'data' =>json_encode($data),
  171. // 'type'=>'5upay',
  172. // 'requestId'=>$data['requestId'],
  173. // 'time'=>time(),
  174. // ));
  175. // }else{
  176. $log -> setLog(array(
  177. 'data' =>json_encode($data),
  178. 'type'=>'5upay',
  179. 'requestId'=>$data['requestId'],
  180. 'time'=>time(),
  181. 'm_id'=>$merchant['m_id']
  182. ));
  183. // }
  184. $res = self::request($data, $url);
  185. $log -> updateDe(array(
  186. 'status' => $res['status'] ?? '',
  187. 'message' => $res['errorMessage']??'',
  188. 'requestId'=>$data['requestId']
  189. ));
  190. return $res;
  191. }catch (Exception $exception){
  192. Db::name("log")->insert(array('data'=>$exception->getFile().':'.$exception->getLine().'【'.$exception->getMessage().'】'));//日志记录
  193. }
  194. }
  195. /**
  196. * 入网查询
  197. * @author lin
  198. * @created 2021/5/14
  199. */
  200. public function merchantQuery($requestId){
  201. $url = env('SHOUXINYI.HOSTNAME') . 'serviceprovider/declaration/query';
  202. $data['merchantId'] = env('SHOUXINYI.MERCHANTID'); // 商户编号
  203. // $data['subMerchantId'] = (string)$subMerchantId;// 二级商户编号
  204. $data['requestId'] = $requestId; //请求订单号
  205. return self::request($data,$url);
  206. }
  207. /**
  208. * 上传图片到sftp
  209. * @param string $path 远程文件夹
  210. * @param string $file 远程路径含文件名
  211. * @param string $file1 本地文件路径
  212. * @return bool
  213. */
  214. public static function uploadSFtp($path,string $file, string $file1): bool
  215. {
  216. $sftp = new SFTP(env('SHOUXINYI.SFTPHOST'),env('SHOUXINYI.SFTPPORT'));
  217. $login = $sftp->login('899104033', RSA::load(file_get_contents(App::getRootPath()."pkey/5upay/899104033")));
  218. if (!$login) {
  219. Db::name("log")->insert(array('data'=>'sftp登录失败'));//日志记录
  220. }
  221. $sftp -> mkdir($path);
  222. return $sftp -> put($file,file_get_contents($file1));
  223. }
  224. /**
  225. * 境内收单
  226. * @param array $datas
  227. * @return
  228. *
  229. */
  230. public static function onlinePay($datas){
  231. $url = 'https://apis.5upay.com/onlinePay/order';
  232. /*
  233. * SCANCODE-ALI_PAY-P2P 支付宝扫码(需要传入clientIp,接收返回的base64编码并解析成二维码图片)
  234. * ALIPAY-WAP-P2P 支付宝H5支付
  235. * ALIPAY-OFFICIAL 支付宝生活号支付
  236. * MINIAPPS-WEIXIN_PAY-P2P 微信小程序 需要IP、openid 传小程序商编
  237. * WECHAT-OFFICIAL_PAY 微信公众号 传公众号商编 需要openid
  238. * SCANCODE-EQRCODE_PAY 易码付 传公众号商编
  239. * */
  240. try {
  241. $data['merchantId'] = env('SHOUXINYI.MERCHANTID'); // 商户编号
  242. $data['requestId'] = $datas['requestId'];//订单号
  243. $data['orderAmount'] = $datas['orderAmount'];//金额。单位分
  244. $data['orderCurrency'] = 'CNY';//交易币种默认CNY(人民币)
  245. // $data['notifyUrl'] = env('APP_URL') . '/api/notify/pay_5upay';//回调地址;//通知地址
  246. $data['notifyUrl'] = env('APP_URL') . $datas['notifyUrl'];//回调地址;//通知地址
  247. $data['callbackUrl'] = $datas['callbackUrl'];//同步地址
  248. $data['remark'] = $datas['remark'] ?? '1';// 备注,通过支付结果返回商户
  249. $data['paymentModeCode'] = $datas['paymentModeCode'];//支付方式编码
  250. switch ($data['paymentModeCode']){
  251. case "MINIAPPS-WEIXIN_PAY-P2P": //微信小程序 需要IP、openid 传小程序商编
  252. $data['appId'] = env('WECHAT_MP.APPID');//小程序appId
  253. $data['reportSerialNo'] = env('SHOUXINYI.REPORT_SREIAL_MP_NO1');//报备序列号
  254. break;
  255. case "SCANCODE-EQRCODE_PAY"://易码付
  256. case "WECHAT-OFFICIAL_PAY"://微信公众号 传公众号商编 需要openid
  257. case "WECHAT-OFFICIAL_PAY-P2P"://微信公众号 传公众号商编 需要openid
  258. $data['appId'] = env('WECHAT.APPID');//微信appId
  259. $data['reportSerialNo'] = env('SHOUXINYI.REPORT_SREIAL_NO1');//报备序列号
  260. break;
  261. }
  262. $data['openId'] = $datas['openId'] ?? '';//微信openId,或者支付宝的buyer_id或者用户的钱包ID,或者微包服务号的openid
  263. $data['productDetails'] = $datas['productDetails'];//商品信息
  264. $data['splitMark'] = $datas['splitMark'] ?? 'DO_SPLIT';//DO_SPLIT 分账,NOT_DO_SPLIT 不分帐 默认分账
  265. $data['payer'] = ['name'=>''];//此json可空,但是不可将此参数整个去掉,需以以下格式传入接口: “payer”:{}
  266. $data['clientIp'] = $datas['clientIp'] ?? '';//客户端外网IP
  267. return self::request($data, $url);
  268. }catch (Exception $exception){
  269. Db::name("log")->insert(array('data'=>$exception->getFile().':'.$exception->getLine().'【'.$exception->getMessage().'】'));//日志记录
  270. return $exception->getFile().':'.$exception->getLine().'【'.$exception->getMessage().'】';
  271. }
  272. }
  273. /**
  274. * 订单查询
  275. * @param array $datas
  276. * @return
  277. */
  278. public static function onlinePay_query($datas){
  279. $url = "https://apis.5upay.com/onlinePay/query";
  280. $data['merchantId'] = env('SHOUXINYI.MERCHANTID'); // 商户编号
  281. $data['requestId'] = $datas['requestId']; //订单号要查询的订单号
  282. return self::request($data, $url);
  283. }
  284. /**
  285. * 分账
  286. * @param array $datas
  287. * @return
  288. */
  289. public static function split($datas){
  290. $url = "https://apis.5upay.com/split/order";
  291. $data['merchantId'] = env('SHOUXINYI.MERCHANTID'); // 商户编号
  292. $data['notifyUrl'] = env('APP_URL') . '/api/notify/pay_split_5upay';//回调地址
  293. $data['requestId'] = $datas['requestId']; //订单号
  294. $data['orderId'] = $datas['orderId'];//交易订单的订单号
  295. $data['totalSplitAmount'] = $datas['totalSplitAmount'];//分账金额
  296. $data['splitDetails'] = $datas['splitDetails'];//分账信息
  297. /*
  298. * splitDetails 分账信息:
  299. * subSplitRequestId: 分账明细订单号
  300. * splitAccType: 分账类型 MERCHANT_ACC 商户;WALLET_ACC 钱包
  301. * splitAccId: 分账类型为商户时此参数传商户的merchantId(可传主商户商编),分账类型为钱包时此参数传用户的walletId
  302. * splitAmount: 分账明细订单金额
  303. * remark: 备注
  304. * */
  305. return self::request($data, $url);
  306. }
  307. /**
  308. * 提现
  309. * @param array $datas
  310. * @return
  311. */
  312. public static function withdrawal($datas){
  313. $url = "https://apis.5upay.com/wallet/withdraw";
  314. $data['partnerId'] = env('SHOUXINYI.MERCHANTID'); // 主商户编号
  315. $data['merchantId'] = $datas['merchantId']; // 商户编号
  316. $data['requestId'] = $datas['requestId']; // 订单编号
  317. $data['withdrawAmount'] = $datas['withdrawAmount']; // 提现金额 分为单位,不带小数点
  318. $data['notifyUrl'] = env('APP_URL') . '/api/notify/withdrawal_5upay';//回调地址
  319. return self::request($data, $url);
  320. }
  321. /**
  322. * 提现查询
  323. * @param array $datas
  324. * @return
  325. */
  326. public static function withdrawQuery($datas){
  327. $url = "https://apis.5upay.com/wallet/withdrawQuery";
  328. $data['partnerId'] = env('SHOUXINYI.MERCHANTID'); // 主商户编号
  329. $data['merchantId'] = $datas['merchantId']; // 商户编号
  330. $data['requestId'] = $datas['requestId']; // 订单编号
  331. return self::request($data, $url);
  332. }
  333. /**
  334. * 查询可提现金额
  335. * @param array $datas
  336. * @return
  337. */
  338. public static function balanceQuery($datas){
  339. $url = "https://apis.5upay.com/member/balanceQuery";
  340. $data['partnerId'] = env('SHOUXINYI.MERCHANTID'); // 主商户编号
  341. $data['merchantId'] = $datas['merchantId']; // 商户编号
  342. $data['requestId'] = $datas['requestId']; // 订单编号
  343. $data['currency'] = $datas['currency']; // 查询币种
  344. $data['dateTime'] = $datas['dateTime']; // 时间戳
  345. $data['accountType'] = $datas['accountType']; // 账户类型
  346. return self::request($data, $url);
  347. }
  348. /**
  349. * 修改银行信息
  350. * @param array $datas
  351. * @return
  352. */
  353. public static function modifyBankCard($datas){
  354. $url = "https://apis.5upay.com/serviceprovider/declaration/modifyBankCard";
  355. $data['merchantId'] = $datas['merchantId']; // 主商户编号
  356. $data['subMerchantId'] = $datas['subMerchantId']; // 二级商户编号
  357. $data['requestId'] = $datas['requestId']; // 订单号
  358. $data['bankCode'] = $datas['bankCode']; // 开户行
  359. $data['bankBranchName'] = $datas['bankBranchName']; // 支行名称
  360. $data['bankCardNo'] = $datas['bankCardNo']; // 开户账号
  361. $data['cityCode'] = $datas['cityCode']; // 市编码
  362. $data['provinceCode'] = $datas['provinceCode']; // 省编码
  363. return self::request($data, $url);
  364. }
  365. /**
  366. * 退款
  367. * @param array $datas
  368. * @return
  369. */
  370. public static function refund($datas,$type=1){
  371. $url = "https://apis.5upay.com/onlinePay/refund";
  372. try {
  373. $data['merchantId'] = env('SHOUXINYI.MERCHANTID'); // 商户编号
  374. $data['notifyUrl'] = env('APP_URL') . '/api/notify/pay_refund_5upay';//回调地址
  375. $data['requestId'] = $datas['requestId']; //订单号
  376. $data['orderId'] = $datas['orderId'];//原交易订单的流水号
  377. $data['amount'] = $datas['amount'];//退款金额
  378. $data['remark'] = '备注';
  379. if($type==1) {
  380. $data['isCollect'] = $datas['isCollect'];
  381. $data['refundCollectDetail'] = $datas['refundCollectDetail'];//归集信息
  382. }
  383. return self::request($data, $url);
  384. }catch (Exception $exception){
  385. Db::name("log")->insert(array('data'=>$exception->getFile().':'.$exception->getLine().'【'.$exception->getMessage().'】'));//日志记录
  386. return $exception->getFile().':'.$exception->getLine().'【'.$exception->getMessage().'】';
  387. }
  388. }
  389. /**
  390. * 签名
  391. * @param $data
  392. * @param int $type 1:其他接口。2:提现接口
  393. * test2
  394. * @return array|bool
  395. * @author lin
  396. * @created 2021/5/13 17:54
  397. */
  398. public static function getSign($data){
  399. //第一步键名首字母排序
  400. //第二步sha1签名
  401. //第三步私钥签名
  402. //第四步生成十六位aes密钥
  403. //第五步data用AES加密
  404. //第六步.公钥加密.加密的数据是第四步生成的AES密钥,防止AES密钥泄露
  405. try {
  406. $sort = self::sortArray($data);//第一步键名首字母排序
  407. $sha1=sha1($sort, true);//第二步sha1签名
  408. $data['hmac'] = self::rsaPrivateSign($sha1,App::getRootPath(). 'pkey/5upay/private.pfx','IDajxBO5');//第三步CFCA私钥签名
  409. $randStr = self::getRandStr(16);//第四步生成十六位aes密钥
  410. $json = json_encode($data);
  411. $body = Aes::encrypt7($json,$randStr);//第五步AES加密
  412. //header
  413. // $header_partnerId = $data['partnerId'];//服务商id.如果业务没有服务商id的话可不传此参数
  414. $headerArray = array(
  415. "Content-type:application/vnd.5upay-v3.0+json",
  416. "merchantId:".$data['merchantId'],//商户编号
  417. "requestId:".$data['requestId'],//商户订单号
  418. "encryptKey:".self::rsaPublicEncode(App::getRootPath(). 'pkey/5upay/test.cer',$randStr)//第六步.CFCA公钥加密.加密的数据是第四步生成的AES密钥,防止AES密钥泄露
  419. );
  420. if(isset($data['partnerId'])){
  421. array_push($headerArray,"partnerId:".$data['partnerId']);
  422. }
  423. return array('body'=>$body,'header'=>$headerArray,'data'=>json_encode($data));
  424. }catch (Exception $exception){
  425. Db::name("log")->insert(array('data'=>$exception->getFile().':'.$exception->getLine().'【'.$exception->getMessage().'】'));//日志记录
  426. return false;
  427. }
  428. }
  429. /**
  430. * CFCA公钥加密
  431. */
  432. public static function rsaPublicEncode($public_key,$rands): string
  433. {
  434. $encryptKey=file_get_contents($public_key);
  435. $pem = chunk_split(base64_encode($encryptKey),64,"\n");//转换为pem格式的公钥
  436. $public_key = "-----BEGIN CERTIFICATE-----\n".$pem."-----END CERTIFICATE-----\n";
  437. $pu_key = openssl_pkey_get_public($public_key);//这个函数可用来判断公钥是否是可用的
  438. openssl_public_encrypt($rands,$encryptKey,$pu_key);//公钥加密
  439. $encryptKey = base64_encode($encryptKey);
  440. return $encryptKey;
  441. }
  442. /**
  443. * CFCA私钥签名
  444. */
  445. public static function rsaPrivateSign($data,$path='',$password=''){
  446. $pubKey = file_get_contents($path);
  447. $results=array();
  448. openssl_pkcs12_read($pubKey,$results,$password);
  449. $private_key=$results['pkey'];
  450. $pi_key = openssl_pkey_get_private($private_key);//这个函数可用来判断私钥是否是可用的,可用返回资源id Resource id
  451. openssl_sign($data, $signature,$private_key,"md5");
  452. $signature=base64_encode($signature);
  453. return $signature;
  454. }
  455. /**
  456. * 随机字符串
  457. */
  458. public static function getRandStr($length){
  459. //字符组合
  460. $str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  461. $len = strlen($str)-1;
  462. $randstr = '';
  463. for ($i=0;$i<$length;$i++) {
  464. $num=mt_rand(0,$len);
  465. $randstr .= $str[$num];
  466. }
  467. return $randstr;
  468. }
  469. /**
  470. * 统一请求
  471. * @return bool|string
  472. * @author php迷途小书童
  473. * @created 2021/5/13 9:15
  474. */
  475. public static function request($data,$url){
  476. try {
  477. $data = self::getSign($data);//签名
  478. Db::name("log")->insert(array('data'=>json_encode($data)));//日志记录
  479. if(!$data) return false;
  480. $postUrl = self::posturl($url,$data['body'],$data['header'],$data['data']);
  481. return self::returnDecode($postUrl);
  482. } catch (\Throwable $e) {
  483. return $e->getMessage();
  484. }
  485. }
  486. /**
  487. * 返回数据解密
  488. * */
  489. public static function returnDecode($postUrl){
  490. //一、CFCA私钥解密
  491. $res = self::rsaPrivateDecode($postUrl['encryptKey'],App::getRootPath(). 'pkey/5upay/private.pfx','IDajxBO5');//CFCA私钥解密
  492. //二、AES解密
  493. $getInfo = Aes::decrypt7($postUrl['data'],$res);
  494. Db::name("log")->insert(array('data'=>json_encode($getInfo)));//日志记录
  495. return $getInfo;
  496. }
  497. /**
  498. * CFCA 私钥解密
  499. * */
  500. public static function rsaPrivateDecode($encryptKey,$private_key,$password){
  501. $prikey=file_get_contents($private_key);
  502. $results=array();
  503. openssl_pkcs12_read($prikey,$results,$password);
  504. $private_key=$results['pkey'];
  505. $pi_key = openssl_pkey_get_public($private_key);
  506. openssl_private_decrypt(base64_decode($encryptKey),$decrypted,$private_key);
  507. return $decrypted;
  508. }
  509. /**
  510. * 字符排序
  511. * @return string
  512. * @author lin
  513. * @created 2021/5/13 17:50
  514. */
  515. public static function sortArray($para=null)
  516. {
  517. // $vars = $para ? '' : get_object_vars($this);
  518. // unset($vars['response_hmac_fields']);
  519. $data = array();
  520. foreach ($para as $k => $var) {
  521. if (is_scalar($var) && $var !== '' && $var !== null) {
  522. $data[$k] = $var;
  523. } else if (is_object($var)) {
  524. $data[$k] = array_filter((array)$var);
  525. } else if (is_array($var)) {
  526. $data[$k] = array_filter($var);
  527. }
  528. if (empty($data[$k])) {
  529. unset($data[$k]);
  530. }
  531. }
  532. ksort($data);
  533. $hmacSource = '';
  534. foreach($data as $key => $value){
  535. if (is_array($value)) {
  536. ksort($value);
  537. foreach ($value as $key2 => $value2) {
  538. if (is_object($value2)) {
  539. $value2 = array_filter((array)$value2);
  540. ksort($value2);
  541. foreach ($value2 as $oKey => $oValue) {
  542. $oValue .= '#';
  543. $hmacSource .= trim($oValue);
  544. }
  545. } else if(is_array($value2)){
  546. ksort($value2);
  547. foreach ($value2 as $key3 => $value3) {
  548. if (is_object($value3)) {
  549. $value3 = array_filter((array)$value3);
  550. ksort($value3);
  551. foreach ($value3 as $oKey => $oValue) {
  552. $oValue .= '#';
  553. $hmacSource .= trim($oValue);
  554. }
  555. } else{
  556. $value3 .= '#';
  557. $hmacSource .= trim($value3);
  558. }
  559. }
  560. } else{
  561. $value2 .= '#';
  562. $hmacSource .= trim($value2);
  563. }
  564. }
  565. } else {
  566. $value .= '#';
  567. $hmacSource .= trim($value);
  568. }
  569. }
  570. return $hmacSource;
  571. }
  572. public static function getOrderNumber($type){
  573. list($msec, $sec) = explode(' ', microtime());
  574. $msectime = number_format((floatval($msec) + floatval($sec)) * 1000, 0, '', '');
  575. return $type . '_' . $msectime . mt_rand(10000, max(intval($msec * 10000) + 10000, 98369));
  576. }
  577. /**
  578. * @return array
  579. */
  580. public static function posturl($url,$body,$headerArray,$data){
  581. // $json = json_encode($data, JSON_UNESCAPED_UNICODE);
  582. $num = md5(time().rand(100,1000));
  583. Db::name("log")->insert(array('data'=>'【'.$url.'】data:'.$data));//日志记录
  584. Db::name("log")->insert(array('data'=>'【'.$url.'】body:'.json_encode($body)));//日志记录
  585. Db::name("log")->insert(array('data'=>'【'.$url.'】headerArray:'.json_encode($headerArray)));//日志记录
  586. $curl = curl_init();
  587. curl_setopt($curl, CURLOPT_URL, $url);
  588. curl_setopt($curl,CURLOPT_HEADER, 1 ); // 过滤HTTP头
  589. curl_setopt($curl,CURLOPT_HTTPHEADER,$headerArray);
  590. curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
  591. curl_setopt($curl,CURLOPT_POST,true); // post传输数据
  592. curl_setopt($curl,CURLOPT_POSTFIELDS,$body);// post传输数据
  593. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//SSL证书认证
  594. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
  595. $output = curl_exec($curl);
  596. curl_close($curl);
  597. Db::name("log")->insert(array('data'=>json_encode($output)));//日志记录
  598. preg_match_all('/(encryptKey|merchantId|data"):(\s+|")([^"\s]+)/s',$output,$m);
  599. list($encryptKey, $merchantId, $data) = $m[3];
  600. $responsedata = array("data" =>$data,"encryptKey"=>$encryptKey,"merchantId"=>$merchantId);
  601. return $responsedata;
  602. }
  603. }