1.前臺請求後臺獲得相應的地址參數,跳轉URL 前臺跳轉支付頁面
(1)前臺
window.document.location = "${ctxPath}/api/alipay.html?_batch=true&_type=alipayPay&_c="+_this.id;
(2)後臺
/**
* 生成跳轉支付寶頁面
*/
public String _$indexBatchAlipayPay() {
Map<String, String> map = alipayPay();
String from = map.get("form");
if ("200".equals(map.get("status"))) {
renderHtml(from);//返回渲染HTML頁面
} else {
try {
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
basePath = basePath + "/member/order?type=route&key=123#/order/unpaid?_c=" + map.get("_c") + "&text=" + URLEncoder.encode(from, "UTF-8");
response.sendRedirect(basePath);
attr("text", from);
} catch (Exception e) {
e.printStackTrace();
logger.error("[{}] [{}] [{}]", "生成跳轉支付寶頁面失敗 請求消息-->" + map.toString(), "<異常-->", e);
}
}
return null;
}
/**
* 生成跳轉支付寶頁面
*
* @author: hjj
* @date: 2018年1月30日 上午11:52:28
* @param: @return:
* void @throws
*/
public Map<String, String> alipayPay() {
Map<String, String> map = new HashMap<>();
map.put("status", "404");
map.put("_c", _c);
if (!StringUtils.isBlank(_c)) {
Members members = getMembers();
OrderForm orderForm = orderFormService.queryCodeObject(_c);
if (orderForm == null || isDel(orderForm) || !Constants.ORDER_FORM_STATUS_BUY_UNPAID.equals(orderForm.getStatus())) {//
map.put("form", "未找到未支付訂單");
return map;
}
if (orderForm.getMembersId() == null || !members.getId().equals(orderForm.getMembersId().getId())) {
map.put("form", "未找到用戶未支付訂單");
return map;
}
RouteInfor routeId = routeInforService.getById(orderForm.getRouteId().getId());
RLock lock = null;
// 鎖庫存
try {
lock = CacheFactory.build(CacheFactory.REDIS).getRLock(Constants.ROUTE_QUOTA_RLOCK + orderForm.getRouteId().getId());
if (Constants.IS_TRUE.equals(routeId.getIsScareBuying())) {// 搶購活動
Boolean boo = routeInforService.judgeScareBuying(routeId.getBuyingStartDate(), routeId.getBuyingEndDate(), new Date());
if (!boo) {// 不在搶購時間內
map.put("form", "搶購時間已過!");
return map;
}
}
int quota = routeId.getQuota() == null ? 0 : routeId.getQuota();
int pendingQuota = routeId.getPendingQuota() == null ? 0 : routeId.getPendingQuota();
Integer amount = orderForm.getAmount() == null ? 0 : orderForm.getAmount();
if ((quota - pendingQuota) <= 0 || (quota - pendingQuota - amount) < 0) {
map.put("form", "庫存不足,請刷新後重新操作!");
return map;
}
// 金額
String totalAmount = orderForm.getAmountPaid().toString();
// 訂單編號 唯一
String outTradeNo = orderForm.getCoding();
// 支付頁面有效時間 默認1分鐘
int TimeoutExpress = 1;
if (Constants.IS_TRUE.equals(orderForm.getIsScareBuying())) {// 搶購
String value = sysParameterService.querySysParameterDate(Constants.SYS_PARAMETER_CODE_SCARE_BUYING_PAGE_TIME);
if (value != null && StringUtils.isNumeric(value)) {
TimeoutExpress = Integer.parseInt(value);
}
} else {// 訂單
String value = sysParameterService.querySysParameterDate(Constants.SYS_PARAMETER_CODE_ORDER_PAGE_TIME);
if (value != null && StringUtils.isNumeric(value)) {
TimeoutExpress = Integer.parseInt(value);
}
}
Cache cache = CacheFactory.build(CacheFactory.EHCACHE_PAGE_EXPIRED);// 緩存對象
Element element = cache.get(Constants.CODE_KEY_ORDER_PAGE_EXPIRED + _c);
App app = WebHelper.getApp(request);
if (element == null) {// 無緩存才鎖庫存
// 嘗試加鎖,最多等待5秒,上鎖以後10秒自動解鎖
boolean res = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!res) {// 加鎖失敗
map.put("form", "系統繁忙,請重新操作!");
return map;
}
cache.set(Constants.CODE_KEY_ORDER_PAGE_EXPIRED + _c, _c, TimeoutExpress * 60);
MemOperateLog memOperateLog = new MemOperateLog();
memOperateLog.setModule("訂單模塊");
memOperateLog.setMembersId(members);
memOperateLog.setContent("生成支付寶頁面,並鎖定庫存");
memOperateLog.setIp(IpUtil.getIpAddr(request));// ip
memOperateLog.setResult(Constants.IS_TRUE);
routeInforService.updateRouteInforQuota(null, routeId.getId(), amount, Constants.ROUTE_PENDING_QUOTA_JIA, members, new Date(), memOperateLog, app);
lock.unlock();
}
HttpServletRequest request = ServletActionContext.getRequest();
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
String returnUrl = basePath + "api/alipay/new.json";
String notifyUrl = basePath + "api/alipay.txt";// .txt後綴會直接輸出resp的message屬性
if (App.isPc(app)) {
// PC
AlipayTradePayModel model = new AlipayTradePayModel();
// model.setBody("Javen IJPay PC支付測試");//訂單描述
model.setTimeoutExpress(TimeoutExpress + "m");// 支付頁面有效時間
model.setOutTradeNo(outTradeNo);
// 銷售產品碼,與支付寶簽約的產品碼名稱。 注:目前僅支持FAST_INSTANT_TRADE_PAY
model.setProductCode("FAST_INSTANT_TRADE_PAY");
model.setTotalAmount(totalAmount);
model.setSubject(orderForm.getRouteName());// 訂單標題
String form = tradePage(response, model, notifyUrl, returnUrl);
map.put("status", "200");
map.put("form", form);
}else{
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setTimeoutExpress(TimeoutExpress + "m");// 支付頁面有效時間
// model.setBody("Javen IJPay PC支付測試");//訂單描述
model.setOutTradeNo(outTradeNo);
// 銷售產品碼,與支付寶簽約的產品碼名稱。 注:目前僅支持FAST_INSTANT_TRADE_PAY
model.setProductCode("QUICK_MSECURITY_PAY");
model.setTotalAmount(totalAmount);
model.setSubject(orderForm.getRouteName());// 訂單標題
String alipay = tradePageMP(response, model, notifyUrl, returnUrl);
map.put("status", "200");
map.put("form", alipay);
}
} catch (Exception e) {
e.printStackTrace();
map.put("form", "系統異常,請重新操作");
logger.error("[{}] [{}]", "生成支付寶頁面異常", e);
if (lock != null && lock.isLocked()) {// 鎖未解開時解開
lock.unlock();
}
}
} else {
map.put("form", "未找到訂單");
}
return map;
}
2.支付完成後有一個直接回調 returnUrl 驗籤回調(頁面及時反饋 ) notifyUrl 異步支付結果回調(支付結果以這個未準)
// 支付寶網關(固定)
private static final String URL = "https://openapi.alipay.com/gateway.do";
//private static final String URL = "https://openapi.alipaydev.com/gateway.do";
// APPID 即創建應用後生成
private static final String APP_ID = "20180";
// private static final String APP_ID = "20160";
// 開發者私鑰,由開發者自己生成
private static final String APP_PRIVATE_KEY = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCVfP76bsfaxaB6ENHLAR5GLJKRZoKqgi7fngYv7+lgu/YnGVwri40y+rKwpNZJ53mnY7KjvgwGVOcgiQ8KFbABHVbmVPqCPG5DxyJ3eZDqppEvDC6agJ0u/jDfnU8VNBvKhfjHJuRa3EQDx05uAlCcHj9M3c9AU37LZWC0qSMWEBpeSh4VNK2+uVS+nhs3m+CYcutXDMMUDqjMkpqGk4CN14Vidsp1e2w/ylo/CcqnsnV+4goD+/VFe4ZVwRPK4KB/GqLEfJEqbckO4RpCLbBWzIE79WoRkuPTZkM5RscZKtSbdS0lEdjvL0Dk++yRpoL9CGuHKDBMj4fp5+im8BWUH03UpfBNtMkssVUzaMM8btIyOsndQf2HWPcAf1J+nAYPNtzl/oMFs7MluE1dz4uSJfEtF8CBbwrFYbdaQOvc5eMLXfYBs3GwF2xAi1k7lplJHZzepYiPK7sFffSXPKaIrK4F6tqfI9YIvC61XEi0wE3m1aBGRMTeU8mG8amB7i132N9b+pMFTAQKBgQDP+8Cl45nJ3DKZkCu67VTIWEmqC6irMQU3NnR3v7f54sWmbPJJITGfbYVclIn1r4e1Ap9hDnRiEtBbxGgfWN6q1Oh5FoYwCoiS2frXolS2vEYbLuRPqx6kwmn6ORErNaC0Hr0leS7rZ4zTIlYK9OEGMxHEPPveFFEx4QwsyT/vgQKBgQC4AA9mtuc7PCeDmX9HoEPLk4A0ihj6P79spZFRugAvoOBRpeoLWkfadfJqvVOeu+FrpNwG268ewTyAq9TbKZIsvhyDmCep/QHRl7k68y4D6n0pxIP2vAp19TPU49PqTmzvK1Bg1zabZNfTrbxk15kvo4AvV/W+3C4Ai4LmzFiV+wKBgDL58oL2YRg/10saMg2z4+bi9NMv3lxvPGcG+s7q963Qrj8XTt8rupzn3BsP8Sx9pFZyWRwRzebH07faUOb2AkV+v6gGJEecHVNZCAlYKUlMpiY9fQol1/HsEMuatyGPGF9tdVC8/goGrFaHgO1YjdrWI5GknNsWwbvm+/PyM7CBAoGAGbnQbXEHNW38rFaqyA3tTKpL04y3X03Iat1fSwjxe7dHhcLnzTyLLU0uXqQn69qi8tTy+nZ+rpMf7XGY6ieOy3itLez5Jrg8J9zJxic3+M4xP8S9GqNB5yooXAyalXjvnCT3MQMGHIXl8y+oVoUaYuG+S2BF+LB6MhNPcwuBGz8CgYBmAmJ6YqAjrE+givbywXhzFmphriY0dvgUwvxaRLZK6NVF5C+UBd3u0eRibvQYuLLba67zk7LnEcg4w1jmyHkgENibroze/FA4DjwgvcGesqQk7OGFrFQWSxEDs5rNjoC8RmT0keo2BwqRvIKzx7QTrccMxP6M/pR6FiIQPl9Txw05==";
// 參數返回格式,只支持json
private static final String FORMAT = "json";
// 編碼集,支持GBK/UTF-8
private static final String CHARSET = "UTF-8";
// 支付寶公鑰,由支付寶生成
private static final String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIc3nVyK0EKDDhI4cJlmEdsiU4xORrtePJ9H3x8Ynh1gXuu9JQmYghuz6qooSokbeSvNsvbAojQOHA9tXbHm639Pb5dKvYs/osntwsGrEpyxxPzUAXBBqZjm9YNCS29IA2j7kiBwChrCZPS9+V3Twd1OvC8qoQgie3cpQ9I98jbn8Dxt1ZsMLAso67PufYQfxrSzimN+uCBm9cUM57G4H1KDrdGRQZ+";
// private static final String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9gh8/e3Z/Vc8X82Mek0GJu+qmulrMtQXsKxgeZ71XsuvXPU0UPNG/EFdyyj4NE7Dh//+N0B3ue0BX87E8rCSefn6Mkss+S4Pi/2jVy3kMwKA/iUtGAjRcT+GM97GABjqWAGQK5A2dFAHeLK/gzTZuZm8k/frlTDM1r4t26uzDu/Civ/GbBCdPy69N7nWYSKSz04xPKQZZZ/uOo9N2Igr/vO7UFQQIDAQAB";
// 商戶生成簽名字符串所使用的簽名算法類型,目前支持RSA2和RSA,推薦使用RSA2
private static final String SIGN_TYPE = "RSA2";
private static final AlipayClient ALIPAY_CLIENT = new DefaultAlipayClient(URL, APP_ID, APP_PRIVATE_KEY, FORMAT, CHARSET, ALIPAY_PUBLIC_KEY, SIGN_TYPE);
/**
* 異步通知處理 //notify_url eg. POST /api/alipay.txt
*
* @author: hjj
* @date: 2018年1月26日 上午10:32:25
* @param: @return:
*/
@Override
public String create() {
// 獲取支付寶POST過來反饋信息 -->
Map<String, String> params = new HashMap<String, String>();
PaymentSubmitLog paymentSubmitLog = new PaymentSubmitLog();// 支付提交日誌
paymentSubmitLog.setChannel(Constants.PAYMENT_CHANNEL_ALIPAY);
paymentSubmitLog.setStatus(Constants.OPERATE_RESULT_FALSE);
String reTxt = "fail";
RLock lock = null;
boolean res = true;
Cache cache = CacheFactory.build(CacheFactory.REDIS);
Members members = new Members();
OrderForm orderForm = new OrderForm();
try {
params = toMap(request);
members.setUserName("支付寶");
boolean verify_result = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, CHARSET, SIGN_TYPE);
String out_trade_no = params.get("out_trade_no");// 訂單編號
String trade_no = params.get("trade_no");// 支付寶交易號
String total_amount = params.get("total_amount") == null ? "0" : params.get("total_amount");// 支付寶交易金額
paymentSubmitLog.setCoding(out_trade_no);
paymentSubmitLog.setSerialNumber(trade_no);
paymentSubmitLog.setAmount(new BigDecimal(total_amount));
if (verify_result) {// 驗籤成功
// 在這裏加上商戶的業務邏輯程序代碼 異步通知可能出現訂單重複通知 需要做去重處理
String trade_status = params.get("trade_status");
if (trade_status != null && trade_status.equals("TRADE_SUCCESS")) {// 支付成功
List<OrderForm> list = orderFormService.queryOrderForm(out_trade_no, null);
if (list != null && list.size() > 0) {// 找到訂單
orderForm = list.get(0);
orderForm.setAmountRealPaid(new BigDecimal(total_amount));// 支付寶交易金額
// 鎖庫存
lock = cache.getRLock(Constants.ROUTE_QUOTA_RLOCK + orderForm.getRouteId().getId());
res = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (res) {// 獲得鎖
paymentSubmitLog = orderFormService.handleAlipayOrder(orderForm, members, params, paymentSubmitLog);
reTxt = "success";// 成功必須返回這個
lock.unlock();
}
} else {
paymentSubmitLog.setRemark("支付成功-->支付寶反饋信息-->" + params.toString() + "未找到訂單訂單編號-->" + out_trade_no);
logger.info("[{}] [{}]", "非法", params.toString());
}
} else {
paymentSubmitLog.setRemark("支付失敗-->支付寶反饋信息-->" + params.toString());
logger.info("[{}] [{}]", "失敗", params.toString());
}
} else {
paymentSubmitLog.setRemark("驗籤失敗-->支付寶反饋信息-->" + params.toString());
logger.info("[{}] [{}]", "非法", params.toString());
}
} catch (Exception e) {
e.printStackTrace();
paymentSubmitLog.setRemark("運行異常-->支付寶反饋信息-->" + params.toString() + "異常信息-->" + e.getMessage());
logger.error("[{}] [{}] [{}]", "異常", params.toString(), e);
if (lock != null && lock.isLocked()) {// 鎖未解開時解開
lock.unlock();
}
}
if (Constants.OPERATE_RESULT_PROBLEM.equals(paymentSubmitLog.getStatus())) {
logger.info("[{}] [{}]", "問題訂單", params.toString());
} else if (Constants.OPERATE_RESULT_TRUE.equals(paymentSubmitLog.getStatus())) {
logger.info("[{}] [{}]", "支付成功", params.toString());
} else if (Constants.OPERATE_RESULT_FALSE.equals(paymentSubmitLog.getStatus())) {
logger.info("[{}] [{}]", "支付失敗", params.toString());
} else if (Constants.OPERATE_RESULT_PENDING.equals(paymentSubmitLog.getStatus())) {
logger.info("[{}] [{}]", "支付待處理", params.toString());
}
paymentSubmitLogService.save(paymentSubmitLog);
if (!res) {
paymentSubmitLog.setStatus(Constants.OPERATE_RESULT_PENDING);
Map<String, Object> params2 = new HashMap<String, Object>();
params2.put("orderType", "alipay");
params2.put("orderForm", orderForm);
params2.put("params", params);
params2.put("members", members);
params2.put("paymentSubmitLogId", paymentSubmitLog.getId());
cache.ladd(Constants.PAYMENT_ORDER_LIST, params2);
}
logger.info("[{}]", "記錄操作日誌成功!!!");
initResult(RespCode.OK, reTxt);
return reTxt;
}
/**
* 同步商戶頁面驗籤,這裏只是返回驗籤的狀態 //return_url get /api/alipay/new.json
*
* @author: hjj
* @date: 2018年1月26日上午10:32:25
* @param: @return:
* void @throws
*/
@Override
public String editNew() {
initResult(RespCode.OK);
Map<String, String> map = toMap(request);
try {
boolean verify_result = AlipaySignature.rsaCheckV1(map, ALIPAY_PUBLIC_KEY, CHARSET, SIGN_TYPE);
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
if (verify_result) {// 驗證成功
String out_trade_no = map.get("out_trade_no");
List<OrderForm> list = orderFormService.queryOrderForm(out_trade_no, null);
if (list != null && list.size() > 0) {
logger.info("[{}] [{}]", "同步商戶頁面驗籤 驗證成功 請求消息-->", map.toString());
basePath = basePath + "/member/order?type=route&key=123#/order/finish?_c=" + list.get(0).getCoding();
} else {
logger.info("[{}] [{}]", "訂單不存在 訂單號-->" + out_trade_no, "<請求消息-->" + map.toString());
}
} else {
logger.info("[{}] [{}]", "同步商戶頁面驗籤 驗證失敗 請求消息-->", map.toString());
}
response.sendRedirect(basePath);
} catch (Exception e) {
// 返回頁面失敗
e.printStackTrace();
logger.error("[{}] [{}] [{}]", "同步商戶頁面驗籤 驗證失敗 請求消息-->" + map.toString(), "<異常-->", e);
}
return "";
}