Refer:https://open.unionpay.com/ajweb/product/detail?id=80
交易步驟:
1、瀏覽並選購商品:用戶通過手機客戶端與商戶系統交互瀏覽選購商品,客戶端向商戶系統發送
購買商品請求數據。
2、生成並推送訂單信息請求數據:商戶系統根據選購商品請求數據生成訂單數據集,把構造完成
的訂單信息數據集合按照移動支付系統的訂單信息推送接口,通過商戶後臺提交(POST)的方式傳遞
給移動支付系統。
3、移動支付系統對請求數據進行處理:移動支付系統得到這些集合後,會先進行安全校驗等驗證,
一系列驗證通過後便會處理這次發送過來的數據請求。
4、返回交易流水號:移動支付系統返回商戶系統交易流水號應答。
5、轉發交易流水號:商戶系統轉發交易流水號至手機客戶端。
6、調用支付控件,發起支付請求數據:手機客戶端收到交易流水號等要素,調起支付控件,用戶
輸入支付信息後,客戶端按照移動支付系統的支付接口規則組裝併發送支付請求數據至移動支付系統。
7、移動支付系統對請求數據進行處理:移動支付系統得到這些集合後,會先進行安全校驗等驗證,
一系列驗證通過後便會處理這次發送過來的數據請求。
8、返回支付結果:移動支付系統返回結果至手機客戶端。
9、返回支付結果:支付控件返回商戶客戶端支付結果(目前爲了兼容控件第一期,未採用frontEndUrl
方式來返回結果)。
10、移動支付系統後臺異步返回處理的結果數據:對於成功處理完成的交易,移動支付系統服務器
主動發起通知,調用商戶在請求時設定好的後臺通知地址路徑(參數backEndUrl), 把支付結果數據反
饋給商戶。
11 商戶對獲取的返回結果數據進行處理:商戶在前臺通知處理頁面(參數frontEndUrl 指定頁面)
或服務器後臺通知頁面(參數backEndUrl 指定頁面)獲取移動支付返回的結果數據後,可以結合自身
網站的業務邏輯進行數據處理(如:訂單更新等操作)。以後臺通知爲準。若未收到後臺通知,需要發
起交易信息查詢請求。
相關說明:
商戶後臺接收到移動支付系統支付成功通知交易後,需返回全渠道系統後臺確認已收到應答。
生成TN
/// <summary>
/// ApplePay支付接口服務
/// </summary>
[RoutePrefix("api/applepay")]
public class ApplePayController : ApiController
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// ApplePay支付接口服務
/// </summary>
private readonly IApplePayService _applePayService;
/// <summary>
/// 構造函數
/// </summary>
/// <param name="applePayService">ApplePay支付接口服務</param>
public ApplePayController(IApplePayService applePayService)
{
_applePayService = applePayService;
}
/// <summary>
/// 生成交易流水號[即TN]
/// </summary>
/// <param name="payTradeNo">參數</param>
/// <returns></returns>
[HttpPost]
[Route("generate_trade_no")]
public async Task<IHttpActionResult> GenerateTradeNoAsync(PayPalData payPal)
{
var data = await _applePayService.GenerateTradeNoAsync(payPal);
return Ok(new { IsError = data.IsError, Msg = data.Msg, Data = data.Data });
}
}
/// <summary>
/// 生成交易流水號[即TN]
/// </summary>
/// <param name="payPal">參數</param>
/// <returns></returns>
public async Task<WebAPIResponse> GenerateTradeNoAsync(PayPalData payPal)
{
try
{
var param = new Dictionary<string, string>();
//版本號
param["version"] = "5.0.0";
//編碼方式
param["encoding"] = "UTF-8";
//交易類型
param["txnType"] = "01";
//交易子類
param["txnSubType"] = "01";
//業務類型
param["bizType"] = "000201";
//簽名方法
param["signMethod"] = "01";
//渠道類型
param["channelType"] = "08";
//接入類型
param["accessType"] = "0";
//前臺通知地址
//param["frontUrl"] = SDKConfig.FrontUrl;
//後臺通知地址
param["backUrl"] = SDKConfig.BackUrl;
//交易幣種
param["currencyCode"] = "156";
//商戶號
param["merId"] = SDKConfig.MerId;
//商戶訂單號,8-32位數字字母,不能含“-”或“_”
param["orderId"] = payPal.OutTradeNO;
//訂單發送時間,參考取法: DateTime.Now.ToString("yyyyMMddHHmmss")
param["txnTime"] = DateTime.Now.ToString("yyyyMMddHHmmss");
//交易金額,單位分
param["txnAmt"] = payPal.TotalFee;
//請求方保留域,透傳字段,查詢、通知、對賬文件中均會原樣出現,如有需要請啓用並修改自己希望透傳的數據
param["reqReserved"] = payPal.Resv;
//簽名
AcpService.Sign(param, System.Text.Encoding.UTF8);
string url = SDKConfig.AppRequestUrl;
var rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8);
if (rspData.Count == 0)
{
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "請求失敗。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8))
{
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "商戶端驗證返回報文簽名失敗。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
string respcode = rspData["respCode"];
if (respcode != "00")
{
logger.Error("失敗:" + rspData["respMsg"] + "。<br>\n");
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "失敗:" + rspData["respMsg"] + "。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = false,
Msg = "後續請將此tn傳給手機開發,由他們用此tn調起控件後完成支付",
Data = rspData["tn"]
};
}).ContinueWith(t => t.Result);
}
catch (Exception ex)
{
logger.Fatal(ex, "GenerateTradeNoAsync Exception: " + ex.Message);
return new WebAPIResponse
{
IsError = false,
Msg = "GenerateTradeNoAsync Exception: " + ex.Message,
Data = string.Empty
};
}
}
異步通知
/// <summary>
/// 蘋果支付
/// </summary>
public class ApplePayController : Controller
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// 支付服務
/// </summary>
private readonly IPayPalService _payPalService;
/// <summary>
/// ApplePay支付服務
/// </summary>
private readonly IApplePayService _applePayService;
/// <summary>
/// 構造函數
/// </summary>
/// <param name="payPalService">整合支付服務</param>
/// <param name="applePayService">ApplePay支付服務</param>
public ApplePayController(IPayPalService payPalService, IApplePayService applePayService)
{
_payPalService = payPalService;
_applePayService = applePayService;
}
/// <summary>
/// ApplePay支付異步回調
/// </summary>
/// <returns></returns>
public async Task<ActionResult> Notify()
{
var param = new Dictionary<string, string>();
//param = @"accessType=0&bizType=000301&certId=69597475696¤cyCode=156&encoding=UTF-8&merId=301310048990295&orderId=000098393411&queryId=201512280008212498278&reqReserved=041601&respCode=00&respMsg=Success!&respTime=20151228000822&settleAmt=10400&settleCurrencyCode=156&settleDate=1228&traceNo=249827&traceTime=20151228000821&txnAmt=10400&txnSubType=01&txnTime=20151228000821&txnType=01&version=3.0.0&signature=cQHM+yW2G34Clkv5zm56XDnd8VhnC1aly5kmjfQ0mUyZb9mN79DJPvC5a98GXwPn7EBn7e1DUNs0J8Nr6FGIXc4A50QltH6njNsiSZMtIR6wlwPqqhga/AprJ4JulL2h6uv0ITPw9b5oHZx81oMpblzM+ZTamP8ZMWmH65ctrl4Qog+U09bXIGOEnezwHYG7Nz8/TumZeSch0TvR0S/vw61u6u45e81FmZ1oarE165QZ+jIfkiRnqI7/iGy4Xa2iSV7qCnNusTgMUg0JVZroBEfr5rA60+0FEzOFzLvae8yVLbsJ454hB4IyaltX34gQJAjpgBZ66dvJo2lGMCe1QQ==".ConvertStringToDictionary();
var item = Request.Form.AllKeys;
for (int i = 0; i < item.Length; i++)
{
param.Add(item[i], Request.Form[item[i]]);
}
//判斷是否有帶返回參數
if (param.Count <= 0)
{
return Json(new { IsError = true, ErrorMsg = "NO DATA !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet);
}
logger.Info("【 ApplePayController Notify SDKUtil.ConvertDictionaryToString : 請求報文=[" + param.ConvertDictionaryToString() + "]\n");
//驗證簽名
if (!AcpService.Validate(param, System.Text.Encoding.UTF8))
{
logger.Error("ApplePayController Notify VERIFY FAIL SDKUtil.ConvertDictionaryToString : " + param.ConvertDictionaryToString());
return Json(new { IsError = true, ErrorMsg = "DATA VERIFY FAIL !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet);
}
//驗證數據 [重要!!!!]
if (!param.ContainsKey("orderId") || !param.ContainsKey("respCode") || !param["respCode"].Equals("00"))
{
return Json(new { IsError = true, ErrorMsg = "DATA VERIFY FAIL !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet);
}
//查詢支付參數【確保來源是合併支付】
var payPal = _payPalService.Query(param["orderId"]);
if (payPal.IsNull() || payPal.NotifyUrl.IsNullOrEmpty() || payPal.OutTradeNO.IsNullOrEmpty())
{
logger.Error("ApplePayController Notify NOT FIND ORDER SDKUtil.ConvertDictionaryToString : " + param.ConvertDictionaryToString());
return Json(new { IsError = true, ErrorMsg = "NOT FIND ORDER !!!", Data = string.Empty }, JsonRequestBehavior.AllowGet);
}
//記錄日誌
payPal.TradeNO = param["queryId"];
//支付金額分轉換元
payPal.TotalFee = (Convert.ToInt32(param["txnAmt"]) / 100M).ToStringSafe();
payPal.Service = payPal.Service + "_Notify";
payPal.CreateTime = DateTime.Now;
payPal.Memo = param.ConvertDictionaryToJson();
payPal.Resv = param["reqReserved"];
payPal.Memo = param.ConvertDictionaryToJson();
bool flag = _payPalService.Save(payPal);
if (!flag)
{
logger.Error("ApplePayController Notify SAVE PayPalData FAIL SDKUtil.ConvertDictionaryToString : " + param.ConvertDictionaryToString());
return Json("DATA SAVE PayPalData FAIL !!!", JsonRequestBehavior.AllowGet);
}
try
{
//調用業務接口
logger.Info("ApplePayController PostJsonAsync req: " + payPal.SerializeJson(System.Text.Encoding.UTF8));
var data = await payPal.NotifyUrl.PostJsonAsync(payPal).ReceiveJson<WebAPIResponse>();
logger.Info("ApplePayController PostJsonAsync resp: " + data.SerializeJson(System.Text.Encoding.UTF8) + " 】");
if (data.IsError)
{
logger.Fatal("!!! ApplePayController PostJsonAsync : " + payPal.SerializeJson(System.Text.Encoding.UTF8));
return Json("DATA PostJsonAsync FAIL FAIL !!!", JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
logger.Fatal(ex, "ApplePayController PostJsonAsync Exception : " + ex.Message + " payPal : " + payPal.SerializeJson(System.Text.Encoding.UTF8));
return Content("exception: " + ex.Message);
}
return await Task.Run(() =>
{
return Content("success");
}).ContinueWith(t => t.Result);
}
}
交易狀態查詢
/// <summary>
/// 交易狀態查詢
/// </summary>
/// <param name="orderNo">訂單號</param>
/// <returns></returns>
public async Task<WebAPIResponse> QueryTradeStatusAsync(string orderNo)
{
try
{
var param = new Dictionary<string, string>();
//版本號
param["version"] = "5.0.0";
//編碼方式
param["encoding"] = "UTF-8";
//證書ID
param["certId"] = CertUtil.GetSignCertId();
//簽名方法
param["signMethod"] = "01";
//交易類型
param["txnType"] = "00";
//交易子類
param["txnSubType"] = "00";
//業務類型
param["bizType"] = "000000";
//接入類型
param["accessType"] = "0";
//渠道類型
param["channelType"] = "07";
//商戶號
param["merId"] = SDKConfig.MerId;
//商戶訂單號,8-32位數字字母,不能含“-”或“_”
param["orderId"] = orderNo;
//訂單發送時間,參考取法: DateTime.Now.ToString("yyyyMMddHHmmss")
param["txnTime"] = DateTime.Now.ToString("yyyyMMddHHmmss");
// 簽名
AcpService.Sign(param, System.Text.Encoding.UTF8);
string url = SDKConfig.SingleQueryUrl;
var rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8);
if (rspData.Count == 0)
{
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "請求失敗。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8))
{
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "商戶端驗證返回報文簽名失敗。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
string respcode = rspData["respCode"];
if (respcode != "00")
{
logger.Error("失敗:" + rspData["respMsg"] + "。<br>\n");
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "失敗:" + rspData["respMsg"] + "。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
string origRespCode = rspData["origRespCode"];
if (origRespCode != "00")
{
logger.Error("稍後查詢:" + rspData["origRespMsg"] + "。<br>\n");
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "稍後查詢:" + rspData["origRespMsg"] + "。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = false,
Msg = "交易成功!!!",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
catch (Exception ex)
{
logger.Fatal(ex, "QueryTradeStatusAsync Exception: " + ex.Message);
return new WebAPIResponse
{
IsError = true,
Msg = "QueryTradeStatusAsync Exception: " + ex.Message,
Data = string.Empty
};
}
}
退貨
/// <summary>
/// 退貨交易
/// </summary>
/// <param name="orderNo">訂單號</param>
/// <param name="origQryId">原消費的queryId</param>
/// <param name="txnAmt">交易金額</param>
/// <returns></returns>
public async Task<WebAPIResponse> RefundTradeNoAsync(string orderNo, string origQryId, string txnAmt)
{
try
{
var param = new Dictionary<string, string>();
//商戶號
param["merId"] = SDKConfig.MerId;
//版本號
param["version"] = "5.0.0";
//編碼方式
param["encoding"] = "UTF-8";
//簽名方法
param["signMethod"] = "01";
//交易類型
param["txnType"] = "04";
//交易子類
param["txnSubType"] = "00";
//業務類型
param["bizType"] = "000201";
//接入類型
param["accessType"] = "0";
//渠道類型
param["channelType"] = "07";
//後臺通知地址
//param["backUrl"] = SDKConfig.BackUrl;
//商戶訂單號,8-32位數字字母,不能含“-”或“_”
param["orderId"] = orderNo;
//原消費的queryId,可以從查詢接口或者通知接口中獲取
param["origQryId"] = origQryId;
//訂單發送時間
param["txnTime"] = DateTime.Now.ToString("yyyyMMddHHmmss");
//交易金額,退貨總金額需要小於等於原消費
param["txnAmt"] = txnAmt;
//請求方保留域,透傳字段,查詢、通知、對賬文件中均會原樣出現,如有需要請啓用並修改自己希望透傳的數據
//param["reqReserved"] = "透傳信息";
// 簽名
AcpService.Sign(param, System.Text.Encoding.UTF8);
string url = SDKConfig.BackTransUrl;
var rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8);
if (rspData.Count == 0)
{
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "請求失敗。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8))
{
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "商戶端驗證返回報文簽名失敗。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
string respcode = rspData["respCode"];
if (respcode != "00")
{
logger.Error("失敗:" + rspData["respMsg"] + "。<br>\n");
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = true,
Msg = "失敗:" + rspData["respMsg"] + "。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
return await Task.Run(() =>
{
return new WebAPIResponse
{
IsError = false,
Msg = "訂單號:" + orderNo + " 退貨受理成功。<br>\n",
Data = string.Empty
};
}).ContinueWith(t => t.Result);
}
catch (Exception ex)
{
logger.Fatal(ex, "RefundTradeNoAsync Exception: " + ex.Message);
return new WebAPIResponse
{
IsError = true,
Msg = "RefundTradeNoAsync Exception: " + ex.Message,
Data = string.Empty
};
}
}