⽀付宝⽀付(⼀)—H5⼿机⽹站⽀付2.0(ade.wap.pay)
  写这篇⽂章的初衷是因为最近项⽬中涉及到较多⽀付模块,于是打算从这篇⽂章开始,陆续整理⼀下⽀付宝和的⽀付模块。每篇⽂末会给出最新整理的⽀付demo,会随着⽂章进度不断更新,⼀⽅⾯是⾃⼰的总结过程,另⼀⽅⾯希望能帮助到更多像我这种⼩⽩的程序汪。其实⽀付宝的官⽅⽂档和demo也很详细,本⽂只是做简要总结。
⼀、Wap⽀付产品介绍
  这篇⽂章我们先来介绍⼀下⽀付宝Wap⽀付(也叫作⼿机⽹站⽀付),⼿机⽹站⽀付功能适⽤于商家在移动端⽹页应⽤中集成⽀付宝⽀付功能。商家在⽹页中调⽤⽀付宝提供的⽹页⽀付接⼝调起⽀付宝客户端内的⽀付模块,商家⽹页会跳转到⽀付宝中完成⽀付,⽀付完后跳回到商家⽹页内,最后展⽰⽀付结果。
  注意:⼿机⽹站⽀付产品不建议在APP端使⽤;如果需要在APP端中使⽤⽀付,请接⼊APP⽀付产品,接⼊⽂档详见。(之后我会介绍APP⽀付)
1、应⽤场景
(1)⽤户已安装⽀付宝⽀付流程
  步骤1:⽤户在浏览器中访问商家⽹页应⽤,选择商品下单、确认购买,进⼊⽀付环节,选择⽀付宝付款,⽤户点击去⽀付,如下图1;
  步骤2:进⼊到⽀付宝⽀付路由页⾯,⽀付宝处理⽀付请求,并尝试唤起⽀付宝客户端,如下图2(此页⽆法⾃定义删除);
  步骤3:进⼊到⽀付宝页⾯,调起⽀付宝⽀付,出现确认⽀付界⾯,如下图3;
  步骤4:⽤户确认收款⽅和⾦额,点击⽴即⽀付后出现输⼊密码界⾯,如下图4;
跑车排名  步骤5:输⼊正确密码后,⽀付宝端显⽰⽀付结果,如下图5;
  步骤6:⾃动回跳到浏览器中,商家根据付款结果个性化展⽰订单处理结果,如下图6。
注意:
  在iOS系统中,唤起⽀付宝App⽀付完成后,不会⾃动回到浏览器或商户App。⽤户可⼿⼯切回到浏览器或商户App。
(2)⽤户未安装⽀付宝⽀付流程
  步骤1:若⽤户未安装⽀付宝客户端,⽤户可先点击⽀付宝⽀付路由页⾯⾥的点这⾥下载⽀付宝APP 蓝⾊链接,下载⽀付宝,如图 7;
  步骤2:成功下载并安装⽀付宝客户端后,点击⽀付宝⽀付路由页⾯⾥的使⽤⽀付宝APP付款按钮进⾏付款,如图 8;
  步骤3:点击使⽤⽀付宝APP付款按钮后,重新开始如上所述的“⽤户已安装⽀付宝⽀付流程”步骤。
注意事项:
  (1)⽀付的时候可以配置退出地址和同步⽀付成功地址,后⾯我会提到
  (2)现在新商户的wap⽀付已经取消了浏览器⽹页登录⽀付宝账号付款的功能,上⾯截图给出的就是新申请的商户⽀付截图,如果是⽼商户或者沙箱环境下,⽀付主页⾯会多⼀个“继续浏览器付款”的选项,⾄于为什么是“继续”,当然是因为wap⽀付是浏览器H5产品调⽤的,哈哈哈
  (3)不要在浏览器中调取⽀付宝WAP⽀付,虽然浏览器也是浏览器,但是我们都知道这两家是死对头的,所以别问我为什么不能
  (4)如果你没有正是环境的产品,可以设置沙箱环境,本⼈就是使⽤的沙箱环境
2、准⼊条件
申请前必须拥有经过实名认证的⽀付宝账户;
企业或个体⼯商户可申请;
需提供真实有效的营业执照,且⽀付宝账户名称需与营业执照主体⼀致;
⽹站能正常访问且页⾯显⽰完整,⽹站需要明确经营内容且有完整的商品信息;
⽹站必须通过ICP备案。如为个体⼯商户,⽹站备案主体需要与⽀付宝账户主体名称⼀致;
如为个体⼯商户,则团购不开放,且古玩、珠宝等奢侈品、投资类⾏业⽆法申请本产品。
3、计费模式
费率按单笔计算;
⼀般⾏业费率:0.6%;⾃2018年5⽉9⽇起,特殊⾏业新签约费率从1.2%调整为1%,特殊⾏业范围包括:休闲游戏;⽹络游戏点卡、渠道代理;游戏系统商;⽹游周边服务、交易平台;⽹游运营商(含⽹页游戏)。
4、使⽤说明
  ⼿机⽹站⽀付产品包含的接⼝和描述如下:
接⼝英⽂名接⼝中⽂名描述
⼿机⽹页⽀付接⼝通过此接⼝传⼊订单参数,同时唤起⽀付宝⼿机⽹页⽀付页⾯
交易关闭接⼝通过此接⼝关闭此前已创建的交易,关闭后,⽤户将⽆法继续付款。仅能关
闭创建后未⽀付的交易。
交易状态查询接⼝通过此接⼝查询某笔交易的状态,交易状态:交易创建,等待买家付款;未付款交易超时关闭,或⽀付完成后全额退款;交易⽀付成功;交易结束,不
可退款。
交易退款接⼝通过此接⼝对单笔交易进⾏退款操作。
退款查询查询退款订单的状态。
账单查询接⼝调⽤此接⼝获取账单的下载链
  注意:其实除了⼿机⽹站⽀付接⼝,其他接⼝对于其他产品都是通⽤的
⼆、准备⼯作
  先上,⽀付宝⽂档⼊⼝模仿最最近做了更新,变得更简洁明了。
  【】
1、创建应⽤并获取APPID
  这个⽐较简单可以参考
  这个部分我就不详细说了,⼀般我们作为开发⼈员不需要⾃⼰去处理这些应⽤申请的事情,会有专门的⼈员申请号相关产品,给到申请完成的⽀付宝账号密码登录,然后我们⾃⼰配置开发环境就ok了。因为申请应⽤会涉及到营业执照、备案域名等等信息要求,⽐较耽误时间,⽽且琐碎,所以这个我们就不⽤⾃⼰操⼼了,做好开发就⾏了。
2、配置应⽤环境
  开发者调⽤接⼝前需要先⽣成RSA密钥,RSA密钥包含应⽤私钥(APP_PRIVATE_KEY)、应⽤公钥(
APP_PUBLIC_KEY)。⽣成密钥后在开放平台管理中⼼进⾏密钥配置,配置完成后可以获取⽀付宝公钥
(ALIPAY_PUBLIC_KEY)。详细步骤请参考。
  不得不说⽀付宝简直是太贴⼼了,为了⽅便开发者⽣成⼀对RSA密钥⽀付宝提供⼀键⽣成⼯具,具体如何⽣成与配置密钥详见。
  下载该⼯具后,解压打开⽂件夹,运⾏“RSA签名验签⼯具.bat”(WINDOWS)或“RSA签名验签⼯具mand”(MAC_OSX)。
  以下演⽰截图
  步骤1:下载⼯具保存在本地并傻⽠式安装,注意安装⽬录不要有空格和中⽂,保持优秀的开发习惯
  步骤2:cmd命令运⾏或者点击桌⾯的快捷⽅式
  步骤3:设置密钥,将⽣成的“公钥字符串”粘贴保存设置,⽀付宝会⽣成对应的“⽀付宝公钥”,保存好你的公私钥信息,后⾯配置⽂件会⽤到。
注意事项:
  ⽣成的私钥需妥善保管,避免遗失,不要泄露。应⽤私钥需填写到代码中供签名时使⽤。应⽤公钥需提供给⽀付宝账号管理者上传到⽀付宝开放平台。
3、配置沙箱环境
  注意:沙箱环境的密钥最好与正式上线的应⽤进⾏区分避免⼀些不必要的⿇烦。WAP⽀付⽀持沙箱环境⽽app⽀付不⽀持沙箱环境
  【】
  上传对应的公钥,沙箱账号待会在测试的时候回使⽤
4、服务端实现(集成并且配置SDK)
  【】
(1)引⼊ 
关于sdk引⼊集成分别有以下两种⽅法
⽅法⼀:直接下载sdk包并修改l⽂件,引⼊sdk,步骤如下:
  步骤1:下载sdk包到本地
  步骤2:maven项⽬中使⽤本地jar包,⾸先我在项⽬根⽬录中创建⼀个lib⽂件夹,将jar包拷贝到lib⽂件夹下
  步骤3:然后我们在maven的l中配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>20161129201425</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/alipay-sdk-java20161129201425.jar</systemPath>
</dependency>
  注意:这⾥的groupId和artifactId以及version都是可以随便填写的,scope必须填写为system,⽽systemPath我们现在我们jar包的地址就可以了
  步骤4:我们必须在maven打包的过程中加⼊我们这个jar包。因为项⽬运⾏的时候需要这个Jar,并且我们得拷贝在WEB-INF/lib⽬录下。若不修改配置的话,打包不进去该sdk
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>${project.basedir}/lib</directory>
<targetPath>WEB-INF/lib</targetPath>
<filtering>false</filtering>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
</webResources>
</configuration>
<version>2.1.1</version>
⽅法⼆:直接maven的l引⼊中央仓库配置,过程如下截图
  步骤1:点击“maven项⽬依赖”
  步骤2:选择你想引⼊的包⽂件,本⼈选择的是“3.3.87.ALL”
  步骤3:复制maven配置到你的l中
(2)集成
  在使⽤SDK调⽤具体API前,需要先配置通⽤接⼊参数
  1、APP_ID 使⽤沙箱模式中的APP_ID.
  2、APP_PRIVATE_KEY ALIPAY_PUBLIC_KEY 使⽤你之前保存的“商户私钥”和⽀付宝给你⽣成的“⽀付宝公钥”
  3、CHARSET 默认使⽤UTF-8
  然后,使⽤上述接⼊参数初始化AlipayClient,这⾥只是描述过程,后⾯会告诉你具体怎么配置
AlipayClient alipayClient = new DefaultAlipayClient("openapi.alipay/gateway.do",APP_ID,APP_PRIVATE_KEY,"json",CHARSET,ALIPAY_PUBLIC_KEY);
  接下来,就可以⽤alipayClient来调⽤具体的API了。
  注意:
  (1)alipayClient只需要初始化⼀次,后续调⽤不同的API都可以使⽤同⼀个alipayClient对象。
  (2)⼿机⽹站⽀付不⽀持第三⽅授权,不能代商家发起请求。
5、调⽤接⼝
  ⼿机⽹站⽀付产品包含两类API:
页⾯跳转类:需要从前端页⾯以Form表单的形式发起请求,浏览器会⾃动跳转⾄⽀付宝的相关页⾯(⼀般是收银台或签约页⾯),⽤户在该页⾯完成相关业务操作后再回跳到商户指定页⾯。例如本产品中的。
  系统调⽤类:直接从服务端发起HTTP请求,⽀付宝会同步返回请求结果。例如本产品中的交易查询等配套API。
6、调⽤流程图
三、服务端具体封装和操作过程
  对于页⾯跳转类API,SDK不会也⽆法像系统调⽤类API⼀样⾃动请求⽀付宝并获得结果,⽽是在接受request请求对象后,为开发者⽣成前台页⾯请求需要的完整form表单的html(包含⾃动提交脚本),商户直接将这个表单的String 输出到http response中即可。
  哈哈,前⾯讲了那么多的准备⼯作,相信你对⽀付宝的wap⽀付流程已经有了⼀个初步的了解,现在我们来看⼀下具体的封装过程
1、将⽀付宝通⽤参数独⽴配置在⼀个属性⽂件中⽅便管理
  ⽀付宝alipay.properties⽂件配置信息如下:
#==================================公司测试账户线下环境====================================
#应⽤ID,您的APPID,收款账号既是您的APPID对应⽀付宝账号
ali_app_id=*********
#商户私钥(应⽤私钥),您的PKCS8格式RSA2私钥
ali_merchant_private_key=*******
#商户公钥(应⽤公钥)
ali_merchant_public_key=*******
#⽀付宝公钥,对应APPID下的⽀付宝公钥。
ali_alipay_public_key=**********
#沙箱账号:uinrgn0730@sandbox  沙箱登录/⽀付密码:111111
#服务器异步通知页⾯路径
ali_notify_url=*******
#页⾯跳转同步通知页⾯路径
ali_return_url=******
#⽤户付款中途退出返回商户⽹站的地址
ali_quit_url=******
#签名⽅式
ali_sign_type=RSA2
#字符编码格式
ali_charset=utf-8
#⽀付宝⽹关(注意这是沙箱的⽹关,正式的⽹关为:openapi.alipay/gateway.do)
ali_gatewayUrl=openapi.alipaydev/gateway.do
#⾓⾊⾝份
ali_pid=2088621954885028
#过期时间
ali_timeout_express=30m
2、读取配置⽂件信息
  AliPayProperties.java⽂件的具体内容如下,与alipay.properties的属性信息⼀致,读取的就是配置⽂件的信息,封装成实体类,⽅便直接使⽤,后续若修改切换⽀付宝信息,只需要修改alipay.properties⽂件信息即可,例如切换沙箱环境为正式环境
fig.properties;
import org.springframework.beans.factory.annotation.Value;
import t.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("classpath:/alipay.properties")
public class AliPayProperties {
/**
* 应⽤ID
*/
@Value("${ali_app_id}")
private String ali_app_id;
/**
* 商户私钥
*/
@Value("${ali_merchant_private_key}")
private String ali_merchant_private_key;
/**
* 商户公钥
*/
@Value("${ali_merchant_public_key}")
private String ali_merchant_public_key;
/
**
* ⽀付宝公钥
*/
@Value("${ali_alipay_public_key}")
private String ali_alipay_public_key;
/**
* 服务器异步通知页⾯路径
*/
@Value("${ali_notify_url}")
private String ali_notify_url;
/**
两弹之父
* 页⾯跳转同步通知页⾯路径
*/
@Value("${ali_return_url}")
private String ali_return_url;
/**
* ⽤户付款中途退出返回商户⽹站的地址
*/
@Value("${ali_quit_url}")
private String ali_quit_url;
/**
* 签名⽅式
*/
@Value("${ali_sign_type}")
private String ali_sign_type;
/**
* 字符编码格式
*/
@Value("${ali_charset}")
private String ali_charset;
/**
* ⽀付宝⽹关
*/
@Value("${ali_gatewayUrl}")
private String ali_gatewayUrl;
/**
* ⾓⾊⾝份
*/
@Value("${ali_pid}")
private String ali_pid;
/**
* 过期时间
*/
@Value("${ali_timeout_express}")
private String ali_timeout_express;
public String getAli_app_id() {
return ali_app_id;
}
public String getAli_merchant_private_key() {
return ali_merchant_private_key;
}
public String getAli_merchant_public_key() {
return ali_merchant_public_key;
}
public String getAli_alipay_public_key() {
return ali_alipay_public_key;
}
public String getAli_notify_url() {
return ali_notify_url;
}
public String getAli_return_url() {
return ali_return_url;
}
public String getAli_quit_url() {
return ali_quit_url;
}
public String getAli_sign_type() {
return ali_sign_type;
}
public String getAli_charset() {
return ali_charset;
}
public String getAli_gatewayUrl() {
return ali_gatewayUrl;
}
public String getAli_pid() {
return ali_pid;
}
public String getAli_timeout_express() {
return ali_timeout_express;
}
public void setAli_app_id(String ali_app_id) {
this.ali_app_id = ali_app_id;
}
public void setAli_merchant_private_key(String ali_merchant_private_key) {
this.ali_merchant_private_key = ali_merchant_private_key;
}
public void setAli_merchant_public_key(String ali_merchant_public_key) {
this.ali_merchant_public_key = ali_merchant_public_key;
}
public void setAli_alipay_public_key(String ali_alipay_public_key) {
this.ali_alipay_public_key = ali_alipay_public_key;
}
public void setAli_notify_url(String ali_notify_url) {
this.ali_notify_url = ali_notify_url;
}
public void setAli_return_url(String ali_return_url) {
this.ali_return_url = ali_return_url;
}
public void setAli_quit_url(String ali_quit_url) {
this.ali_quit_url = ali_quit_url;
}
public void setAli_sign_type(String ali_sign_type) {
this.ali_sign_type = ali_sign_type;
}
public void setAli_charset(String ali_charset) {
this.ali_charset = ali_charset;
}
public void setAli_gatewayUrl(String ali_gatewayUrl) {
this.ali_gatewayUrl = ali_gatewayUrl;
}
public void setAli_pid(String ali_pid) {
this.ali_pid = ali_pid;
}
public void setAli_timeout_express(String ali_timeout_express) {
this.ali_timeout_express = ali_timeout_express;
}
@Override
public String toString() {
return"AliPayProperties [ali_app_id=" + ali_app_id + ", ali_merchant_private_key=" + ali_merchant_private_key
+ ", ali_merchant_public_key=" + ali_merchant_public_key + ", ali_alipay_public_key="
+ ali_alipay_public_key + ", ali_notify_url=" + ali_notify_url + ", ali_return_url=" + ali_return_url
+ ", ali_quit_url=" + ali_quit_url + ", ali_sign_type=" + ali_sign_type + ", ali_charset=" + ali_charset
+ ", ali_gatewayUrl=" + ali_gatewayUrl + ", ali_pid=" + ali_pid + ", ali_timeout_express="
+ ali_timeout_express + "]";
}
}
3、加载接⼊参数并初始化AlipayClient
  AliPayUtil.java的实例化代码信息如下:
ample.learn.utils.pay.alipay;
/**
* ⽀付宝⼯具类
* @author Administrator
*
*/
@Component
public class AliPayUtil {
@Autowired
private AliPayProperties aliPayProperties ;
private static AliPayProperties staticAliPayProperties;
static AlipayClient alipayClient;
/**
* 初始化
*/
@PostConstruct
public void init() {
staticAliPayProperties = aliPayProperties;
alipayClient = new Ali_gatewayUrl(), Ali_app_id(), Ali_merchant_private_key(), "json", Ali_charset(), Ali_alipa    }
}
注意:
  (1)alipayClient只需要初始化⼀次,后续调⽤不同的API都可以使⽤同⼀个alipayClient对象。
  (2)我为了后⾯在业务层中直接调取该静态API⾥⾯封装好的⽅法(后⾯会给出对应⽅法),将其定义为了⼀个⼯具类,⽽该⼯具类需要注⼊读取 AliPayProperties ⾥⾯的属性,所以需要在该类上打上 @Component 注解
  (3)我为了后⾯将其他⽀付API全部定义为静态⽅法直接调⽤,这⾥初始化的时候将 alipayClient 定义为了静态变量,所以需要将 AliPayProperties 也定义为静态变量,但是由于 @Autowired 注解不能作⽤于静态变量,所以借助静
态变量 staticAliPayProperties 作为过渡,并在初始化的⽅法⾥⾯,让 staticAliPayProperties = aliPayProperties;
4、利⽤sdk封装wap⽀付代码
  通⽤模块,就在demo⾥⾯的 AliPayUtil.java ⾥⾯
  /**
* ade.wap.pay:H5⼿机⽹站⽀付接⼝2.0(外部商户创建订单并⽀付)
* @param out_trade_no:商户订单号
* @param total_amount:⽀付⾦额,单位:元
* @return
*/
public static String alipayTradeWapPay(String out_trade_no, String total_amount){
try {
//(1)封izmodel信息
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
//SDK已经封装掉了公共参数,这⾥只需要传⼊业务参数。以下⽅法为sdk的model⼊参⽅式(model和biz_content同时存在的情况下取biz_content)。
model.setOutTradeNo(out_trade_no);
model.setSubject("⽀付宝⼿机⽹站⽀付");
model.setBody("⽀付宝⼿机⽹站⽀付");
model.setProductCode("QUICK_WAP_WAY");
model.setTotalAmount(total_amount);
model.Ali_timeout_express());
model.Ali_quit_url());
//(2)设置请求参数
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
alipayRequest.Ali_return_url());
alipayRequest.Ali_notify_url());
alipayRequest.setBizModel(model);
//(3)请求
String form = alipayClient.pageExecute(alipayRequest).getBody();
System.out.println("*********************\n返回结果为:"+form);金荷娜 张根硕
return form;
} catch (AlipayApiException e) {
e.printStackTrace();
return null;
}
}
5、模拟⽀付请求
  步骤1:控制层 AliPayController.java 写请求⽅法 alipayTradeWapPay
  步骤2:调取的接⼝和实现类
注意:
  (1)这⾥我随机⽣成了商户订单号 out_trade_no,交易⾦额 total_amount 写死了,正常业务处理中,我们是根据你的业务实际情况处理
  (2)⾥⾯涉及到的其他的⼯具类,最后的demo⾥⾯会提供。当然,你也可以⾃⼰随机商城,保持不重复的原则即可
  (3)实际操作中,在调取之前,我们需要处理具体的业务逻辑,因为本⽂只总结⽀付宝相关的,就不补充了。例如:订单⾦额计算处理;商户订单和⽀付宝订单的关联和存储,商户订单和⽀付宝订单可能是多对⼀的关系;⽀付宝
订单需要保存,因为在回调的时候你需要根据⽀付宝订单的 out_trade_no 识别⽀付宝通知的订单结果;你可以把它存储在redis或者持久化到数据库中。
  步骤3:模拟⽀付请求
  这⾥,我是写了⼀个html页⾯,通过前端请求到后台调取控制层⽅法模拟实际⽀付请求的
  对于页⾯跳转类API,SDK不会也⽆法像系统调⽤类API⼀样⾃动请求⽀付宝并获得结果,⽽是在接受request请求对象后,为开发者⽣成前台页⾯请求需要的完整form表单的html(包含⾃动提交脚本),对于返回的该form表单,通
常有两种⽅式处理,⼀种是后端,⼀种是前端
  前端:获取后端返回的form数据对象,取到表单对象并直接⾃动提交到⽀付宝,也就是上⾯和demo中的这种处理⽅式
  后端:商户直接将这个表单的String输出到http response中即可,处理⽅式可以如下(以下两种⽅式是我在项⽬过程中的处理,并不在此次demo中,仅供参考),处理差异和细节已经重点标出来了
  ⽅法⼀:
  ⽅法⼆:
  注意的是:这种处理⽅式的请求⼀般不是上⾯的json请求模式,⽽是直接get页⾯请求跳转的模式
6、运⾏查看测试结果
  右键运⾏demo中的 PayDemoApplication.java ⽂件,浏览器输⼊ localhost:8080查看,当然你也可以修改application.properties配置⽂件中的端⼝号
  点击“H5⼿机⽹站⽀付”按钮,其他按钮功能开发后⾯有时间会发博⽂介绍,不过demo⾥⾯已经有了,你也可以⾃⼰先测试,请求后控制台⽇志显⽰
  详细form内容如下:
<form name="punchout_form" method="post" action="openapi.alipaydev/gateway.do?charset=utf-8&ade.wap.pay&sign=ctYtQCctKweZCxjDAn8D2y4VxnjAI6uu0SYtIaxkPxf2LskIT4fakZF5xciExeHJW5dbOdiujHltgjaTMK9ikxcdDpDiRhH <input type="hidden" name="biz_content" value="{"body":"⽀付宝⼿机⽹站⽀付","out_trade_no":"20200801164810929688437","product_code":"QUICK_WAP_WAY","quit_url":"h <input type="submit" value="⽴即⽀付" >
</form>
<script>document.forms[0].submit();</script>
  我们可以很清楚的看到,⽀付宝返回的是⼀个form表单的内容,我们在前端接收后按照上⾯前端的处理⽅法处理后,发现点击“H5⼿机⽹站⽀付”按钮之后,直接跳到了⽀付宝下⾯页⾯,按照以下步骤操作就⾏
注意事项:
  (1)由于我⽤的是沙箱环境,所以输⼊的是沙箱买家账号和密码测试的,出来的页⾯有“继续浏览器付款”和“使⽤⽀付宝APP付款”两个选项,如果选择⽀付宝APP付款需要在⼿机上安装沙箱钱包才⾏。现在新商户接⼊的正式环境调
取页⾯是没有这个选项的,必须安装⽀付宝钱包的,具体区别在产品介绍模块有提到。页⾯如下:
  (2)在测试的时候我们发现如果返回不⽀付(退出、取消⽀付等)和⽀付完成之后,⽀付宝跳到了相关的页⾯,这个原因我们在后⾯同步异步回调模块会介绍到。
四、同异步结果处理
  你以为在成功调取⽀付宝页⾯⽀付了,我们的操作就结束了么,远远没有,因为我们发现我们只是调取了⽀付宝的⽀付页⾯,关于⽀付响应和结果,也就是是否⽀付了,⽀付是否成功了,我们并不知道。聪明的⽀付宝早就为我们考
虑到了,所以他为我们提供了三个参数来处理,我们回过头来看看配置⽂件和调取⽅法,分别如下:
1、quit_url:⽤户付款中途退出返回商户⽹站的地址,⼿机⽹站⽀付选填参数
  (1)设置要求:完整的路径名,携带或
  (2)参数说明:添加该参数后在 h5⽀付收银台会出现返回按钮,可⽤于⽤户付款中途退出并返回到该参数指定的商户⽹站地址。
  (3)注意:该参数对⽀付宝钱包标准收银台下的跳转不⽣效。2019年8⽉份后⼿机⽹站⽀付未安装⽀付宝钱包,默认提⽰下载钱包,不⾛H5页⾯⽀付。该参数设置⽆效。
2、return_url:页⾯跳转同步通知页⾯路径
  (1)设置要求:完整的路径名,携带或
  (2)参数说明:添加该参数后,⽀付宝会以get的⽅式向该地址发送通知,核实同步页⾯是否有跳转,⼿机⽹站⽀付接⼝如果同步页⾯没有进⾏跳转是不会发送对应的同步回调数据内容。
  (3)注意:不能以同步的跳转作为⽀付结果的判断,⽀付宝最终的⽀付结果必须以异步通知或者商户主动查询结果为主,如果同步没有跳转,参考以下⽹址排查: 
3、notify_url:服务器异步通知页⾯路径
  (1)设置要求:⼿机⽹站⽀付接⼝的参数notify_url必须是公⽹能post访问的接⼝地址;不建议加⾃定义参数,异步通知可通过请求参数设置passback_params公⽤回传参数,该参数异步通知返回。
  (2)参数说明:添加该参数之后,⽀付宝会以post⽅式发送⽀付结果通知,需要在异步地址页⾯使⽤post⽅式进⾏接收。
  (3)注意:如果没有收到⽀付宝的异步通知,参考以下⽹址排查:
1、同步回调通知
  步骤1:设计⽀付宝同步通知的controller⼊⼝,接收⽀付宝的通知八一建军节祝福词
  注意:该controller的⼊⼝路径必须与你的配置⽂件⾥⾯的return_url保持⼀致,并且⽀持get接收
  步骤2:设计接⼝和实现类验签,做你想做的
  其中涉及到的⼯具类如下:
ample.learn.utils.string;
asl冰桶挑战import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
public class RequestMap {
/**
* 从request中获得参数Map,并返回可读的Map
*
* @param request
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Map getParameterMap(HttpServletRequest request) {
// 参数Map
Map properties = ParameterMap();
/
/ 返回值Map
Map returnMap = new HashMap();
Iterator entries = Set().iterator();
Map.Entry entry;
String name = "";
String value = "";
while (entries.hasNext()) {
entry = (Map.Entry) ();
name = (String) Key();
Object valueObj = Value();
if(null == valueObj){
value = "";
}else if(valueObj instanceof String[]){
String[] values = (String[])valueObj;中国石化加油卡充值
for(int i=0;i<values.length;i++){
value = values[i] + ",";
}
value = value.substring(0, value.length()-1);
}else{
value = String();
}
returnMap.put(name, value);
}
return returnMap;
}
}
  注意:在同步通知中,我们⼀般只需要验签,根据验签结果返回到具体页⾯,不做其他处理,千万不能以⽀付宝的同步通知结果作为⽀付结果判断成功与否。在我测试过程中,发现在沙箱环境下,同步通知⼀直显⽰验签失败,但是
异步成功,验签⽅法都⼀样,当然不是我的问题了,问了阿⾥的技术,沙箱环境的bug,沙箱测试环境不稳定这个很正常。
  我么可以观察⼀下⽀付宝同步通知给了什么参数信息过来,信息如下:
{
  charset=utf-8,
  out_trade_no=20200801170240119363676,
  ade.urn,
  total_amount=0.01,
  sign=FZol3JVgDcbgNtFIxCACmnTj8ASYllvTJK8cNi2a2sgj0Q7vZ4Z56fI0qnR7fP12p7GAlLldGdHrYI4lHE8dyxVc2Mp4hHNGs4NgzbQluocYhajSR7rejJxjLuF+rkGQiJd5c1ixyIXDUEXeIT3sOShs6m+139DNvUpcfk/YxY07uNZxKBsJfIS2kT/WFZk4hH3/QD3hHwv   trade_no=2020080122001491140510105364,
  auth_app_id=2021000116673834,
  version=1.0,
  app_id=2021000116673834,
  sign_type=RSA2,
  seller_id=2088621954885028,
  timestamp=2020-08-01 17:02:57