目录:
- 场景介绍
- 接入指引
2.1 准备工作
2.2 接口调用规范
2.2.1 请求说明 - API列表
3.1 支付宝微信医保支付下单
3.2 查询医保卡信息
3.3 获取绑卡链接
3.4 微信授权、支付宝实名信息查询
3.5 交易结果查询接口
3.6 交易退款接口
3.7 交易退款结果查询接口
3.8 交易结果通知 - 附录
1. 场景介绍
医保患者通过手机微信输入一次支付密码,即可完成医保、自费的混合支付,便捷就医无需排队。
2. 接入指引
2.1 准备工作
第一步、获取开发者数据
调用聚合支付平台接口,需要与平台商务对接后,获取开发者ID(developId),密钥(privateKey),公钥(publicKey)和对称密钥(desKey),这些参数让商户拥有调用平台接口的权限,对请求的数据进行加密以保证数据的安全性。
- 开发者ID(developerId):
developerId在所有的请求中都需要传入,与密钥公钥信息相对应,鉴别请求是否是由平台商户发起。
- 密钥(privateKey):
密钥在接口请求中用于对数据进行RSA加密,生成签名sign。
- 公钥(publicKey):
公钥用于对签名进行解密
- 对称秘钥(desKey):
<body>标签的中的参数,需要通过des进行对称加解密,请求时使用密文,返回密文通过对称秘钥进行解密。
第二步、获取平台商户号
每个商户对接聚合支付平台,在第三方平台想要拥有的支付能力,需要在支付宝或者微信签约对应的支付方式,平台的商务会为每个商户分配一个平台商户号(merchantNo),该商户号账户下所有的交易流水都会进入到对应的支付宝和微信的账户下,并且平台账单流水都是以商户号进行隔离,这是一个非常重要的参数。
第三步、下载demo
- java版本:下载demo
2.2 接口调用规范
2.2.1 请求说明
接口通信地址:
测试环境:http://onlinetest.bsoftpay.com/gatewayOnline/gateway/portal/execute
正式环境:由平台商务对接人员提供http请求格式:
Content-type:application/xml
实体对象为 xml 格式,严格区分大小写,xml格式如下:
<?xml version='1.0' encoding='UTF-8'?> <Root> <Head></Head> <Body> </Body> </Root>
公共参数说明:
参数 | 类型 | 是否必填 | 最大长度 | 描述 | 示例 |
---|---|---|---|---|---|
MerchantNo | String | 是 | 10 | 注意:这是开发者id(developerId) |
12345 |
FuncId | String | 是 | 20 | 功能码 | P7005 |
Signature | String | 是 | 100 | 签名 | Sa/hb9jaTZZZq89mN62DDooLv/YTvfE9ayrbzgOMsucfIPZfJGmhUia6YWK8vQ5/NQana3Pub7BDswEenm0sBg== |
Timestamp | String | 是 | 100 | 当前时间戳(自1970-1-01 00:00:00.000 到当前时刻的时间距离【精确到毫秒】) | 1564120486130 |
RetCode | String | 是 | 30 | 0000:表示接口业务调用正常,9999:表示处理失败,如参数错误,验签失败等(注:RetCode并不表示交易行为成功或失败,某笔交易是否成功请根据对应接口返回报文的状态字段来判定) | 0000 |
RetMsg | String | 是 | 100 | 接口调用异常返回信息 | 验签失败 |
注意:Signature签名的生成过程,是先通过‘对称秘钥’对<Body>中的数据域进行DES加密,得到密文之后,再用‘私钥’对密文进行RSA加密
请求报文示例:
<?xml version='1.0' encoding='UTF-8'?> <Root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P7006</FuncId> <Timestamp>12312312312324</Timestamp> <Signature>kLRyaxQXe6yLu+ktZUOiB+afVgeY5Doq9fTB27rAkW7/MBMK10tcxWM7eVVtmzxju0Xi1rtW16D7cV5PgM+zDA==</Signature> </Head> <Body>ql1e6Mhtmrq7yA6wGuE4biskwAEq8ALzOFr5kJxbVoP7VuaeoasTxHkdf8GTwlLihUhSk+ZcGsnJWNJHfMCtr9valAWD/y8sOAQut1/sWhg=</Body> </Root>
返回报文示例:
<?xml version="1.0" encoding="UTF-8"?> <Root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P7006</FuncId> <Timestamp>1533718324024</Timestamp> <Signature><![CDATA[hNCUjy30FibXT8YuqhEwaMFv6dzERMRWAVa0XjcKDmzjZ jkQ1DPXzJtJooe43QfLLmW+38eDLmeUK7Y17nMfjA==]]> </Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body> <![CDATA[NLB9nF8xXHoJgCgwYtRkMG9vhJlpSl9hjIF6LMQduYoaNNwmDqKKhBfUVBn u6ZPH33q3DLWEfnzUP5E2Mvzz2gK7riCvIfe1yJ/D5b3Ia67vFkHI6HBPhykm6uVLsbdW 7Q1xSLuEv2jGqfwzSl35GPAJrwrd8wylCRp6B2aLv4x3DYmUROKXVk+fPObHTb0nU7a1Z Hkw7Nhj+15CPHnMLDj/s4jK9OOdkhKrWg/NTDvnZR65jFXsvqPfitPmkYsTEzuXwQSq88GF 9YAmP+dcVeHHxBpaYkcV0N+JdN9h1TJg+uENaZIgU4ayDF2N7dVhYO54+vgSd/3HLHloow zXUVo2XdPVZgrvDRpmyqpk3YA8Pe4njAgZnA7/RRXvqX1ueOyQVEt1br7i4F5mGwnSuM9 KOCDIkTNre9rvCdbsui5U0CpxgeM3WQ==]]> </Body> </Root>
注意:这里的返回数据域为密文,需要接入方使用‘对称秘钥’进行DES解密,即可得到原文数据。如果需要验证数据的有效性,可以利用‘公钥’对Signature进行验签
注意:返回信息中 MerchantNo 是 开发者id
利用java工具类解密示例:
String xml = SecurityUtil.decryptDes(Body,[your desKey], "utf-8");
利用java工具类验签示例:
boolean verify = SecurityUtil.verifyRSA(data, [your publicKey], Signature, "utf-8");
3. API列表
3.1 支付宝微信医保支付下单
- 功能码:P8005
- 接口说明:该接口提供微信医保支付下单,可以同时通过自费+医保的方式进行支付
- 请求参数:
<xml>
参数 | 名称 | 类型 | 长度 | 必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
merchant_no | 收款商户号 | String | 30 | 是 | 创业聚合支付系统为医院分配的平台商户号 | 809900000001 |
sub_merchant | 分店号 | String | 30 | 否 | 商户内部区分订单字段 | 001 |
order_no | 业务单号 | String | 30 | 是 | 商户业务系统的唯一订单号 | BX20190722902141 |
paytype | 支付渠道 | String | 2 | 是 | 2=支付宝,3=微信 | 3 |
trade_type | 交易类型 | String | 2 | 是 | 6=医保线上支付 | 6 |
openid | 用户唯一标识 | String | 128 | 是 | 微信公众号用户(openid);支付宝生活号用户(uid) | obzBc1MlzBaU_kjt6eJvdSqs2qbA |
total_amt | 支付金额 | Price | 20.2 | 是 | 以元为单位,商品总价 自费金额+医保金额 |
0.01 |
timeout_express | 支付超时时间 | Integer | 4 | 否 | 以“分钟”为单位,缺省5分钟,不能小于5 | 6 |
spbill_create_ip | 用户IP | String | 10 | 是 | 客户端实际外网ip 微信必填 |
183.156.115.113 |
order_subject | 商品标题 | String | 150 | 是 | 订单信息 | 深圳平乐骨伤科医院微信医保支付 |
order_detail | 商品详情 | String | 150 | 否 | 订单详情 支付宝必填 |
支付宝医保支付 |
order_type | 医保业务类型 | String | 2 | 是 | 1:挂号 2:诊间 3:住院 支付宝 4:非医院 支付宝 |
1 |
card_org_no | 用户医保卡机构编号 | String | 2 | 否 | 查询用户医保获取 支付宝必传 |
SZHRSS |
pay_way | 支付方式 | String | 2 | 否 | 1:自费 2:医保 3:自费+医保(缺省) 总金额必填 传 1:自费金额必填 传 2:医保金额必填 传 3: 自费和医保金额必填 |
3 |
cash_amt | 自费金额 | Price | 20.2 | 否 | 以元为单位 | 0.01 |
cash_reduced_amt | 医院减免的自费部分金额 | Price | 20.2 | 否 | 以元为单位 | 0.01 |
allow_fee_change | 是否允许预结算费用发生变化 | String | 2 | 否 | 0:不允许(缺省) 1:允许 | 0 |
return_url | 支付后回跳的页面 | String | 200 | 是 | 不论成功失败均会回跳(建议在链接中加上支付单号,方便查询订单状态)微信有效 |
http://xxx.merchantno.com/pay/returnurl |
consume_type | 医保部分扣费类型 | String | 2 | 否 | 默认:0:统筹+个账 1:个账 2:统筹 微信医保字段 |
0 |
insurance_amt | 医保金额 | Price | 20.2 | 否 | 以元为单位 微信医保支付必填 |
0.01 |
card_type | 证件类型 | String | 2 | 否 | 详见证件类型 医保支付必填 |
1 |
card_no | 证件号 | String | 30 | 否 | 证件号码 医保支付必填 |
10001199901011111 |
user_name | 真实姓名 | String | 30 | 否 | 医保姓名 医保支付必填 |
张三 |
mobile | 用户手机号 | String | 30 | 否 | 手机号 | 13211112222 |
child_card_no | 少儿证件号 | String | 30 | 否 | 少儿证件号码 少儿医保支付必填 |
10001199901011111 |
child_user_name | 少儿真实姓名 | String | 30 | 否 | 少儿医保姓名 少儿医保支付必填 |
张四 |
org_no | 医疗机构编码 | String | 64 | 是 | 人社局提供的医疗机构编码 | 123456 |
org_name | 医疗机构名称 | String | 64 | 是 | 医疗机构名称 | 深圳平乐骨伤科医院 |
request_content | 医保请求结构体(透传体) | String | 2000 | 否 | 1 挂号支付必传 2 诊间支付分两种情况 ①线上预结算模式必填 ②线下预结算模式非必填 医保支付必填 |
{"registration_order_pay":{"yljgbm":" 医院编号","ylzh":"医疗证号","czybm":"操作员编码"... |
bill_no | 业务单据号 | String | 32 | 否 | 诊间支付线下预结算模式必传 |
123456 |
extends | 扩展字段 | String | 200 | 否 | 扩展字段 | |
city_id | 城市id | String | 32 | 否 | 可不填,默认为公众号申请接入的城市 | 440300 |
payment_extends | 平台扩展字段 | String | 2000 | 否 | 用于各个省市医保需要的增补字段 | {“channelNo”:”1234”,”isDept”:”4”,”consumeType”:0,”serialNo”:”1234”,”useSandbox”:1} |
notifyURL | 自定义支付结果通知地址 | String | 否 | 单独为此订单配置的支付结果通知地址 | http://test.com/callback |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
return_code | 返回状态码 | String | 16 | 是 | SUCCESS代表成功 FAIL 代表失败 | SUCCESS |
return_msg | 返回信息 | String | 128 | 否 | 返回信息 | OK |
med_trans_id | 微信诊疗单id | String | 64 | 是 | 微信生成的医疗订单 id | 11111 |
pay_url | 支付链接 | String | 256 | 是 | 下单后跳转到此 url,用户完成支付 | weixin://xxx |
pay_appid | 支付小程序 | String | 64 | 否 | 当使用医院小程序进行下单时,会返回此参数,医院小程序跳转至该支付小程序进行支付 | 11111 |
pay_text | 支付宝小程序支付字符串 | String | 1024 | 否 | 支付宝小程序支付字符串 | xxxx |
Java请求示例:
String data = "<xml>\n" + " <order_no>mpTest202104191550216000</order_no>\n" + " <paytype>3</paytype>\n" + " <openid>obzBc1MlzBaU_kjt6eJvdSqs2qbA</openid>\n" + " <merchant_no>109900130000</merchant_no>\n" + " <trade_type>6</trade_type>\n" + " <order_type>1</order_type>\n" + " <pay_way>2</pay_way>\n" + " <total_amt>0.01</total_amt>\n" + " <cash_amt>0.0</cash_amt>\n" + " <spbill_create_ip>127.0.0.1</spbill_create_ip>\n" + " <order_subject>挂号支付</order_subject>\n" + " <return_url>http://xxx.merchantno.com/pay/returnurl</return_url>\n" + " <insurance_amt>0.01</insurance_amt>\n" + " <card_type>1</card_type>\n" + " <card_no>患者身份证号</card_no>\n" + " <user_name>患者姓名</user_name>\n" + " <org_no>123</org_no>\n" + " <org_name>123</org_name>\n" + " <request_content>{}</request_content>\n" + " <city_id>511700</city_id>\n" + " <timeout_express>6</timeout_express>\n" + " <payment_extends>{\"channelNo\":\"AAGG2rOJ0Qvc5-4FUWYrur81\",\"isDept\":\"4\",\"consumeType\":0}</payment_extends>\n" + "</xml>" String desKey = ""; HeaderDTO header = new HeaderDTO(); header.setFuncId("P8005"); header.setMerchantNo(developerId); header.setTimestamp(System.currentTimeMillis() + ""); header.setSignature(SecurityUtil.signRSA(data, privateKey, "UTF-8")); DataDTO dataDTO = new DataDTO(); dataDTO.setHeader(header); dataDTO.setBody(SecurityUtil.encryptDes(data, desKey , "UTF-8")); String content = JaxbMapper.toXml(dataDTO, DataDTO.class, "UTF-8"); String resData = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://onlinetest.bsoftpay.com/gatewayOnline/gateway/portal/execute").setEntity(new StringEntity(content, ContentType.APPLICATION_XML.withCharset(Consts.UTF_8))).build()); String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8); System.out.println(res); //解析返回xml DataDTO resultData = JaxbMapper.fromXml(res, DataDTO.class); //回调成功-获取body内容 if ("0000".equals(resultData.getHeader().getRetCode())) { //解密body resData = SecurityUtil.decryptDes(resultData.getBody(), desKey,"UTF-8"); }else{ //异常信息 resData = resultData.getHeader().getRetMsg(); } } catch (Exception e) { e.printStackTrace(); } System.out.println(resData);
返回示例
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P8005</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
注意:返回参数中MerchantNo 是开发者id
3.2 支付宝微信查询医保卡信息
功能码:P8007
接口说明:该接口提供微信查询医保卡信息
请求参数:
<xml>
参数 | 名称 | 类型 | 长度 | 必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
merchant_no | 收款商户号 | String | 30 | 是 | 创业聚合支付系统为医院分配的平台商户号 | 809900000001 |
pay_channel | 支付通道 | String | 1 | 是 | 2支付宝 3微信 | 2 |
name | 用户名称 | String | 32 | 是 | 用户姓名 | 张三 |
idcard | 用户身份证 | String | 64 | 否 | 证件号码,目前只支持身份证 | 44030019000101123x |
openid | 用户唯一标识 | String | 128 | 是 | 公众号支付是微信用户的(openid);支付宝uid | obzBc1MlzBaU_kjt6eJvdSqs2qbA |
child_name | 少儿实名 | String | 32 | 否 | 需要查询少儿时填入 | |
child_idcard | 少儿身份证 | String | 64 | 否 | 需要查询少儿时填入 |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
return_code | 返回状态码 | String | 16 | 是 | SUCCESS代表成功 FAIL 代表失败 | SUCCESS |
return_msg | 返回信息 | String | 128 | 否 | 返回信息 | OK |
need_bind | 是否需要绑卡 | Boolean | 是 | true/false判断是否调用获取绑卡链接接口 | ||
medical_card_no | 社保卡号 | String | 32 | 否 | 社保卡号 | BBS70130X |
medical_card_id | 医疗证号 | String | 32 | 否 | 用户的医疗证号,对应ylzh字段 | 6055168908 |
medical_comp_no | 电脑号 | String | 32 | 否 | 用户的社保电脑号为空表示无此数据 | 11111 |
medical_insurance_type | 参保类型 | String | 16 | 否 | 用户的参保类型,为空表示未知 1:基本医疗保险(一档) 2:基本医疗保险(二档) 3:特殊 4:基本医疗保险(三档) 5:医疗保险二档(少儿) 6:统筹保险 |
1 |
medical_user_id | 用户医保平台id | String | 32 | 否 | 用户医保平台id | |
city_id | 社保卡绑定的城市ID | String | 32 | 否 | 社保卡绑定的城市ID | |
child_medical_card_id | 少儿医疗证号 | String | 32 | 否 | 输入中有少儿信息时返回,可能为空 | |
child_medical_card_no | 少儿社保卡号 | String | 32 | 否 | 输入中有少儿信息时返回,可能为空 | |
aggreement_no | 签约协议号 | String | 32 | 否 | 支付宝有效。必输 |
|
buyer_user_id | 买家支付宝账用户号 | String | 32 | 否 | 支付宝有效。必输 |
|
medical_card_type | 医保类型 | String | 32 | 否 | 市医保:CITY_INS; 省医保:PROVINCE_INS; 县医保:COUNTY_INS 支付宝有效。必输 |
|
out_user_name | 姓名 | String | 32 | 否 | 持卡人的姓名(脱敏)支付宝有效。必输 |
|
out_user_card_no | 持卡人证件号(脱敏) | String | 32 | 否 | 支付宝有效 |
|
card_org_name | 卡颁发机构名称 | String | 32 | 否 | 支付宝有效。必输 |
|
card_org_no | 卡颁发机构编号 | String | 32 | 否 | 支付宝有效。必输 |
|
extend_params | 业务扩展参数 | String | 32 | 否 | 支付宝有效 |
|
extend_cards | 支付宝账号绑定的其他医保卡信息 (除本人外) | String | 32 | 否 | 支付宝有效 |
|
–|need_bind | 是否需要绑卡 | Boolean | 是 | true/false判断是否调用获取绑卡链接接口 | ||
–|medical_card_id | 医疗证号 | String | 32 | 否 | 用户的医疗证号 | 6055168908 |
–|medical_card_no | 社保卡号 | String | 32 | 否 | 社保卡号 | BBS70130X |
–| medical_card_type | 医保类型 | String | 32 | 否 | 市医保:CITY_INS; 省医保:PROVINCE_INS; 县医保:COUNTY_INS | |
–|out_user_name | 姓名 | String | 32 | 否 | 持卡人的姓名(脱敏) | |
–|out_user_card_no | 证件号(脱敏) | String | 32 | 否 | 持卡人的证件号 | |
–|card_org_no | 机构编号 | String | 32 | 否 | 医保卡颁发机构编号 | |
–|city_id | 城市编码 | String | 32 | 否 | 440100 | |
–|city_id | 城市编码 | String | 32 | 否 | 440100 | |
–|extend_params | 业务扩展参数 | String | 32 | 否 |
Java请求示例:
String data ="<xml>" + "<openid></openid>" + "<merchant_no>809900000001</merchant_no>" + "<pay_channel>3</pay_channel>" + "<name></name>" + "<idcard_md5></idcard_md5>" + "</xml>"; String desKey = ""; HeaderDTO header = new HeaderDTO(); header.setFuncId("P8007"); header.setMerchantNo(developerId); header.setTimestamp(System.currentTimeMillis() + ""); header.setSignature(SecurityUtil.signRSA(data, privateKey, "UTF-8")); DataDTO dataDTO = new DataDTO(); dataDTO.setHeader(header); dataDTO.setBody(SecurityUtil.encryptDes(data, desKey , "UTF-8")); String content = JaxbMapper.toXml(dataDTO, DataDTO.class, "UTF-8"); String resData = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://onlinetest.bsoftpay.com/gatewayOnline/gateway/portal/execute").setEntity(new StringEntity(content, ContentType.APPLICATION_XML.withCharset(Consts.UTF_8))).build()); String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8); System.out.println(res); //解析返回xml DataDTO resultData = JaxbMapper.fromXml(res, DataDTO.class); //回调成功-获取body内容 if ("0000".equals(resultData.getHeader().getRetCode())) { //解密body resData = SecurityUtil.decryptDes(resultData.getBody(), desKey,"UTF-8"); }else{ //异常信息 resData = resultData.getHeader().getRetMsg(); } } catch (Exception e) { e.printStackTrace(); } System.out.println(resData);
返回示例
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P8007</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
注意:返回信息中 MerchantNo 是 开发者id
3.3 微信获取绑卡链接
功能码:P8008
接口说明:该接口提供微信获取绑卡链接
请求参数:
<xml>
参数 | 名称 | 类型 | 长度 | 必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
merchant_no | 收款商户号 | String | 30 | 是 | 创业聚合支付系统为医院分配的平台商户号 | 809900000001 |
pay_channel | 支付通道 | String | 1 | 是 | 2支付宝 3微信 | 2 |
openid | 用户唯一标识 | String | 128 | 否 | 公众号支付是微信用户的(openid)微信医保支付必填 |
obzBc1MlzBaU_kjt6eJvdSqs2qbA |
url | 绑卡完成后的跳转url | String | 512 | 否 | 微信医保支付必填 |
|
from | 来源统计 | String | 512 | 否 | 来源统计 | |
city_id | 城市ID | String | 32 | 否 | 城市ID |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
return_code | 返回状态码 | String | 16 | 是 | SUCCESS代表成功 FAIL 代表失败 | SUCCESS |
return_msg | 返回信息 | String | 128 | 否 | 返回信息 | OK |
bind_url | 绑卡链接 | String | 512 | 否 | 绑卡链接微信医保支付必输 |
|
expires_in | 有效时间 | String | 32 | 否 | 单位:秒 微信医保支付必输 |
3600 |
bind_appid | 绑卡跳转小程序的appid | String | 64 | 否 | 如果调用方为小程序则会返回该参数 |
Java请求示例:
String data = "<xml>" + "<openid></openid>" + "<merchant_no></merchant_no>" + "<pay_channel>3</pay_channel>" + "<url></url>" + "</xml>"; String desKey = ""; HeaderDTO header = new HeaderDTO(); header.setFuncId("P8008"); header.setMerchantNo(developerId); header.setTimestamp(System.currentTimeMillis() + ""); header.setSignature(SecurityUtil.signRSA(data, privateKey, "UTF-8")); DataDTO dataDTO = new DataDTO(); dataDTO.setHeader(header); dataDTO.setBody(SecurityUtil.encryptDes(data, desKey , "UTF-8")); String content = JaxbMapper.toXml(dataDTO, DataDTO.class, "UTF-8"); String resData = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://onlinetest.bsoftpay.com/gatewayOnline/gateway/portal/execute").setEntity(new StringEntity(content, ContentType.APPLICATION_XML.withCharset(Consts.UTF_8))).build()); String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8); System.out.println(res); //解析返回xml DataDTO resultData = JaxbMapper.fromXml(res, DataDTO.class); //回调成功-获取body内容 if ("0000".equals(resultData.getHeader().getRetCode())) { //解密body resData = SecurityUtil.decryptDes(resultData.getBody(), desKey,"UTF-8"); }else{ //异常信息 resData = resultData.getHeader().getRetMsg(); } } catch (Exception e) { e.printStackTrace(); } System.out.println(resData);
返回示例
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P8008</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
注意:返回参数 MerchantNo 是 开发者id
3.4 微信授权、支付宝实名信息查询
- 功能码:P8009
- 接口说明:该接口提供微信授权查询
- 请求参数:
<xml>
参数 | 名称 | 类型 | 长度 | 必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
merchant_no | 收款商户号 | String | 30 | 是 | 创业聚合支付系统为医院分配的平台商户号 | 809900000001 |
pay_channel | 支付通道 | String | 1 | 是 | 2支付宝 3微信 | 2 |
openid | 用户唯一标识 | String | 128 | 是 | 微信必填 公众号支付是微信用户的(openid) |
obzBc1MlzBaU_kjt6eJvdSqs2qbA |
authCode | 微信授权码 | String | 512 | 是 | 微信必填 微信免密授权回调页面返回的authCode 支付宝必填 支付宝返回的accessToken |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
code | 返回状态码 | String | 16 | 是 | 微信成功时返回: 0 成功,小于0 系统错误,大于0 逻辑错误 |
0 |
message | 返回信息 | String | 128 | 否 | 返回信息 | OK |
mi_card_type | 用户授权类型 | String | 128 | 否 | 微信成功时返回: 1”CERITIFICATE”=国家电子凭证 2 ”DEPART”=人社部电子社卡 3 ”LOCAL”=地方卡 |
|
user_name | 用户姓名 | String | 128 | 否 | 用户姓名 | 3600 |
userCardNo | 用户证件号 | String | 128 | 否 | 用户证件号 | 3600 |
auth_code | 电子凭证授权码 | String | 128 | 否 | 电子凭证授权码 | |
city_id | 用户参保地代码 | String | 128 | 否 | 微信成功时返回: 仅当mi_card_type=CERITIFICATE 时,才返回有效值,多地参保用户若未选择主参地,则该字段未空,并且不可使用后续业务。; |
|
mi_card_no | 医保卡号 | String | 128 | 否 | 微信成功时返回: 仅当mi_card_type=LOCAL 时,才返回医保卡号 |
|
contract_no | 签发号 | String | 128 | 否 | 微信成功时返回: 仅当mi_card_type=DEPART 时,才返回签发号 |
|
mi_card_channel | 签发渠道号 | String | 128 | 否 | 微信成功时返回: 当mi_card_type=DEPART 时,返回渠道号 |
- 请求示例:
<xml> <openid>o10ldt4o_0j6v4DzW0y0eAIgc8xg</openid> <authCode>1234</authCode> <merchant_no>109900130000</merchant_no> <pay_channel>3</pay_channel> </xml>
- 返回示例
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P8008</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
注意:返回参数 MerchantNo 是 开发者id
3.5 交易结果查询接口
- 功能码:P2005
- 接口说明:通过该接口获取某个订单的支付结果
- 请求参数:
<Data>
参数 | 名称 | 类型 | 长度 | 是否必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
order_no | 业务单号 | String | 32 | 是 | 商户内部业务单号 | WX2019072200001 |
merchant_no | 收款商户号 | String | 32 | 是 | 商户号 | 809900000001 |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
TradeNo | 平台流水号 | String | 32 | 是 | 聚合支付平台流水号 | 34411000 |
PayTradeNo | 渠道流水号 | String | 32 | 是 | 各个支付渠道返回的流水号 | 4200000376201907220000000000 |
OrderNo | 业务单号 | String | 32 | 是 | 商户内部业务单号 | WX2019072200001 |
Amt | 实际支付金额 | Price | 16.2 | 是 | 用户实际付款金额 | 0.01 |
PayTime | 实际支付时间 | String | 16 | 是 | yyyy-MM-dd hh:mm:ss | 2019-07-22 21:37:31 |
PayStatus | 支付状态 | String | 6 | 是 | 0=未支付;1=支付成功;2=支付失败;-1=关闭或撤销 | 1 |
receipt_amount | 商户收到的金额 | Price | 16.2 | 是 | 相当于订单支付金额 医保支付中为自费金额 |
0.01 |
InsuranceAmt | 医保支付金额 当insurance_self_fee 和 insurance_fund_fee 存在时InsuranceAmt=insurance_self_fee+ insurance_fund_fee | Price | 16.2 | 否 | 使用了医保时返回 医保支付 |
0.01 |
CashAmt | 自费支付金额 | Price | 16.2 | 否 | 使用了医保时返回 医保支付 |
0.01 |
insurance_self_fee | 医保个人账户支付金额 | Price | 16.2 | 否 | 使用了医保时返回 医保支付 |
0.01 |
insurance_fund_fee | 医保统筹账户支付金额 | Price | 16.2 | 否 | 使用了医保时返回 医保支付 |
0.01 |
ReducedAmt | 优惠金额 | Price | 16.2 | 否 | 下单使用了优惠金额时返回 医保支付 |
0.01 |
refundAmt | 退款金额 | Price | 16.2 | 医保订单 存在退费时存在 | 退款金额 | 0.01 |
insurance_bill_no | 医保结算单号 | string | 32 | 医保结算单号 | *02100121051300000186 | |
response_content | 医保返回的结算信息 | string | 1024 | 医保返回的结算信息 | {“XXXXXX”:”YYYYY”} |
JAVA请求示例:
String data = "<Data>" + "<order_no>WX2019072200001</order_no>\n" + "<merchant_no>809900000001</merchant_no>\n"+ "</Data>"; String desKey = ""; HeaderDTO header = new HeaderDTO(); header.setFuncId("P2005"); header.setMerchantNo(developerId); header.setTimestamp(System.currentTimeMillis() + ""); header.setSignature(SecurityUtil.signRSA(data, privateKey, "UTF-8")); DataDTO dataDTO = new DataDTO(); dataDTO.setHeader(header); dataDTO.setBody(SecurityUtil.encryptDes(data, desKey , "UTF-8")); String content = JaxbMapper.toXml(dataDTO, DataDTO.class, "UTF-8"); String resData = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://onlinetest.bsoftpay.com/gatewayOnline/gateway/portal/execute").setEntity(new StringEntity(content, ContentType.APPLICATION_XML.withCharset(Consts.UTF_8))).build()); String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8); System.out.println(res); //解析返回xml DataDTO resultData = JaxbMapper.fromXml(res, DataDTO.class); //回调成功-获取body内容 if ("0000".equals(resultData.getHeader().getRetCode())) { //解密body resData = SecurityUtil.decryptDes(resultData.getBody(), desKey,"UTF-8"); }else{ //异常信息 resData = resultData.getHeader().getRetMsg(); } } catch (Exception e) { e.printStackTrace(); } System.out.println(resData);
返回示例:
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P2005</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
注意: P2005功能中请求参数是用
<Data></Data>
包围,不是<xml></xml>
注意: 返回参数中 MerchantNo 是 开发者id
3.6 交易退款接口
功能码:P2008
接口说明:当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家(原路返回)。注意的地方:微信超过1年的订单无法提交退款,支付宝超过3个月的订单无法提交退款,可以一笔订单多次退款,但是累计金额不能超过原支付金额
请求参数:
<Data>
参数 | 名称 | 类型 | 长度 | 是否必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
RequestNo | 退款订单号 | String | 32 | 是 | 商户内部唯一退款单号 | TK20190722001 |
PayType | 退款渠道 | String | 2 | 是 | 2=支付宝;3=微信 | 2 |
Amt | 退款金额 | Price | 16.2 | 是 | 退款金额不得超过原支付金额 微信医保退款可不传,支付宝自费部分退款必传 |
0.01 |
TradeNo | 原支付业务单号 | String | 32 | 是 | 商户原支付订单号 | WX2019072200001 |
merchant_no | 商户号 | String | 32 | 是 | 平台商户号 | 809900000001 |
cancel_bill_no | 撤销单据号 | String | 20 | 否 | 医保诊间退款必填 人社无要求则由医院自定义 |
109900000001 |
part_refund_type | 部分退款 | String | 2 | 否 | 医保退款相关 1:自费部分退款 2:只退医保(只能全退) 3:单退医保个账部分 4:单退医保统筹部分(只能全退) 不填写医保和现金一起退 |
1 |
request_content | 透传人社 | String | 否 | 医保退款相关 有需要透传数据到人社时填写 |
注:微信医保退款,全额退还自费部分金额或者退还自费+医保的总支付金额,无法自定义金额进行多次退款
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
RequestNo | 退款订单号 | String | 32 | 是 | 申请退款订单号 | TK20190722001 |
Amt | 退款金额 | Price | 16.2 | 是 | 申请退款金额 | 0.01 |
JAVA请求示例:
String data = "<Data>" + "<RequestNo>TK123127893456798765491</RequestNo>" + "<PayType>3</PayType>" + "<Amt>0.01</Amt>" + "<TradeNo>YMP011494322477660</TradeNo>" + "<merchant_no>809900000001</merchant_no>" + "<cancel_bill_no>10000000000000000001</cancel_bill_no>" + "</Data>"; String desKey = ""; HeaderDTO header = new HeaderDTO(); header.setFuncId("P2008"); header.setMerchantNo(developerId); header.setTimestamp(System.currentTimeMillis() + ""); header.setSignature(SecurityUtil.signRSA(data, privateKey, "UTF-8")); DataDTO dataDTO = new DataDTO(); dataDTO.setHeader(header); dataDTO.setBody(SecurityUtil.encryptDes(data, desKey , "UTF-8")); String content = JaxbMapper.toXml(dataDTO, DataDTO.class, "UTF-8"); String resData = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://onlinetest.bsoftpay.com/gatewayOnline/gateway/portal/execute").setEntity(new StringEntity(content, ContentType.APPLICATION_XML.withCharset(Consts.UTF_8))).build()); String res = EntityUtils.toString(response.getEntity(), Consts.UTF_8); System.out.println(res); //解析返回xml DataDTO resultData = JaxbMapper.fromXml(res, DataDTO.class); //回调成功-获取body内容 if ("0000".equals(resultData.getHeader().getRetCode())) { //解密body resData = SecurityUtil.decryptDes(resultData.getBody(), desKey,"UTF-8"); }else{ //异常信息 resData = resultData.getHeader().getRetMsg(); } } catch (Exception e) { e.printStackTrace(); } System.out.println(resData);
返回示例:
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>开发者id</MerchantNo> <FuncId>P2008</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
注意: P2008功能中请求参数是用
<Data></Data>
包围,不是<xml></xml>
注意: 返回信息中 MerchantNo 是 开发者id
3.7 交易退款结果查询接口
功能码:P2009
接口说明:提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用微信零钱或者支付宝余额宝、花呗等支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态(20分钟、3 个工作日都是大概时间,不一定为准,有可能提前,有可能延迟)
请求参数:
<Data>
参数 | 名称 | 类型 | 长度 | 是否必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
RequestNo | 退款订单号 | String | 32 | 是 | 商户内部唯一退款单号 | TK20190722001 |
PayType | 退款渠道 | String | 2 | 是 | 2=支付宝;3=微信 | 2 |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
RequestNo | 退款订单号 | String | 32 | 是 | 申请退款订单号 | TK20190722001 |
Amt | 退款金额 | Price | 16.2 | 是 | 当code为1时这里会显示退款金额 | 0.01 |
CashRefundAmt | 自费退款金额 | Price | 16.2 | 是 | 当code为1时这里会显示退款金额 | 0.01 |
InsuranceRefundAmt | 医保退款金额 | Price | 16.2 | 是 | 当code为1时这里会显示退款金额 | 0.01 |
Code | 退款状态 | String | 2 | 是 | -1交易不存在 1 成功 2 失败 | 1 |
Msg | 错误信息描述 | String | 100 | 是 | 当code为2时这里会显示错误信息 | 0.01 |
- 返回示例:
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>22290</MerchantNo> <FuncId>P2009</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
- 注意: P2009功能中请求参数是用
<Data></Data>
包围,不是<xml></xml>
- 注意:
返回信息中 MerchantNo 是 开发者id
3.8 交易结果通知
- 功能码:P3001
- 接口说明:支付状态改变(支付或作废)后,平台异步通知用户端(平台不保证异步通知能够准时到达,建议接入方有自己的定时主动查询策略,以异步通知作为辅助查询)。交易结果通知的地址,需要提前告知平台商务对接人员进行配置。
注意:商户在接受到支付平台回调后,如果业务成功,返回 success
,失败返回 fail
,注意都是小写,不要用空格或者换行。
支付平台服务器在没有收到业务成功回调信息时,会尝试重发消息,总共3
次通知(通知的间隔频率一般是:30s,1m,2m),3 次以后将不再通知,所以不保证通知最终能成功(建议以交易结果查询接口为主,异步通知为辅,如果 3 秒左右还没有接受到异步通知,就应该主动调用交易结果查询接口)
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
TradeNo | 支付平台交易流水号 | String | 32 | 是 | 支付平台交易流水号 | 32013134 |
PayTradeNo | 渠道流水号 | String | 32 | 是 | 第三方支付渠道流水号 | 20192323971391918391121 |
OrderNo | 业务单号 | String | 32 | 是 | 商户内部业务单号 | wx201907150001 |
Amt | 支付金额 医保订单为总金额 | Price | 16.2 | 是 | 支付金额 | 0.01 |
CashAmt | 自费支付金额 | Price | 16.2 | 医保订单时存在 | 自费支付金额 | 0.01 |
InsuranceAmt | 医保支付金额 当insurance_self_fee 和 insurance_fund_fee 存在时InsuranceAmt=insurance_self_fee+ insurance_fund_fee | Price | 16.2 | 医保订单时存在 | 医保支付金额 | 0.01 |
insurance_self_fee | 医保个人账户支付金额 | Price | 16.2 | 否 | 使用了医保时返回 医保支付 |
0.01 |
insurance_fund_fee | 医保统筹账户支付金额 | Price | 16.2 | 否 | 使用了医保时返回 医保支付 |
0.01 |
PayTime | 实际支付时间 | String | 16 | 是 | yyyy-MM-dd hh:mm:ss | 2019-07-25 12:30:12 |
PayStatus | 支付状态 | String | 6 | 是 | 0=未支付;1=支付成功;2=支付失败;-1=关闭或撤销 | 1 |
insurance_bill_no | 医保结算单号 | String | 32 | 否 | *02100121051300000186 | |
response_content | 医保返回的结算信息 | string | 1024 | 医保返回的结算信息 | {“XXXXXX”:”YYYYY”} |
- 返回示例:
<?xml version="1.0" encoding="UTF-8"?><root> <Head> <MerchantNo>22290</MerchantNo> <FuncId>P3001</FuncId> <Timestamp>1566981115522</Timestamp> <Signature>B5nbh7acyJdeoD663ndVpY/rHwX1HEZR+of+Oy47R6KioObX6jCajtFc3PpVJyDwPhLHUH12EEl/cdDnV+VI6A==</Signature> <RetCode>0000</RetCode> <RetMsg></RetMsg> </Head> <Body></Body> </root>
注:返回参数数据在<body>已经被DES加密,需要接入方利用对接时获取的“对称密钥”,对密文使用下载的加解密工具进行解密
注意:返回信息中MerchantNo 是 开发者id
4. 附录
- 证件类型字典:
证件类型 | 证件名 | 适用说明 |
---|---|---|
1 | 身份证 | 通用 |
2 | 护照 | 支付宝 |
3 | 军官证 | 支付宝 |
4 | 士兵证 | 支付宝 |
5 | 户口本 | 通用 |
6 | 警官证 | 支付宝 |
7 | 台湾居民来往大陆通行证(台胞证) | 通用 |
8 | 香港居民来往内地通行证(回乡证) | 通用 |
9 | 澳门居民来往内地通行证(回乡证) | 通用 |
10 | 临时身份证 | 支付宝 |
11 | 港澳通行证 | 支付宝 |
12 | 营业执照 | 支付宝 |
13 | 外国人永久居留证 | 微信 |
14 | 外 国 人 护 照 | 微信 |