如何使用Stripe与ASP.NET Core Web API集成Gpay等钱包/UPI支付
ASP.NET Core Web API集成Google Pay、PhonePe、Paytm支付方案
Stripe支持Google Pay作为支付方式,但PhonePe、Paytm属于印度本土独立支付网关,需直接对接其官方API。以下是三种支付方式的具体实现代码:
一、Google Pay集成(基于Stripe)
需修改现有Stripe服务,支持指定google_pay作为支付方式类型,前端需配合使用支付Intent的ClientSecret完成支付确认。
1. 调整支付请求模型与Stripe服务
// 扩展支持多支付方式的请求模型 public class StripePaymentRequest { public string CustomerId { get; set; } public string Email { get; set; } public string Description { get; set; } public string Currency { get; set; } public long Amount { get; set; } // 单位为最小货币单位(如印度卢比=派萨) public string PaymentMethodType { get; set; } // 可选值:card、google_pay等 } // 更新StripeService中的支付方法 public async Task<PaymentIntent> AddStripePaymentAsync(StripePaymentRequest request, CancellationToken ct) { var options = new PaymentIntentCreateOptions { Customer = request.CustomerId, Description = request.Description, Amount = request.Amount, Currency = request.Currency, PaymentMethodTypes = new List<string> { request.PaymentMethodType }, ReceiptEmail = request.Email, }; var service = new PaymentIntentService(); return await service.CreateAsync(options, ct); }
2. 调用代码替换原卡片支付逻辑
try { var paymentRequest = new StripePaymentRequest { CustomerId = customerId, Email = paymentDetails.emailAddress, Description = paymentDetails.Description, Currency = paymentDetails.Currency, // 示例:"inr" Amount = paymentDetails.Amount, // 注意转换为最小单位(如100卢比=10000派萨) PaymentMethodType = "google_pay" }; var paymentIntent = await _stripeService.AddStripePaymentAsync(paymentRequest, ct).ConfigureAwait(false); return paymentIntent.ClientSecret; // 返回给前端完成支付确认 } catch (Exception exp) { // 建议添加日志记录,不要直接返回null return null; }
二、PhonePe UPI支付集成
PhonePe提供独立REST API,需完成签名生成、支付发起、回调验证三个核心步骤。
1. 配置参数(appsettings.json)
"PhonePeSettings": { "MerchantId": "你的商户ID", "SaltKey": "你的盐值", "SaltIndex": "1", "BaseUrl": "https://api.phonepe.com/apis/hermes", "CallbackUrl": "https://你的公网域名/api/payments/phonepe-callback" }
2. 支付发起与回调处理代码
// PhonePe支付请求模型 public class PhonePePaymentRequest { public string MerchantTransactionId { get; set; } public long Amount { get; set; } // 单位:派萨 public string MerchantUserId { get; set; } } // PhonePe支付服务 public class PhonePePaymentService { private readonly IConfiguration _config; public PhonePePaymentService(IConfiguration config) { _config = config; } public async Task<string> InitiatePaymentAsync(PhonePePaymentRequest request, CancellationToken ct) { var merchantId = _config["PhonePeSettings:MerchantId"]; var saltKey = _config["PhonePeSettings:SaltKey"]; var baseUrl = _config["PhonePeSettings:BaseUrl"]; var callbackUrl = _config["PhonePeSettings:CallbackUrl"]; // 构建支付请求体 var paymentData = new { merchantId = merchantId, merchantTransactionId = request.MerchantTransactionId, merchantUserId = request.MerchantUserId, amount = request.Amount, callbackUrl = callbackUrl, paymentInstrument = new { type = "UPI", targetApp = "com.phonepe.app" } }; // 生成签名 var payload = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(paymentData))); var signatureRaw = $"{payload}/{request.MerchantTransactionId}/{saltKey}"; var signature = HashSHA256(signatureRaw) + "###" + _config["PhonePeSettings:SaltIndex"]; // 发起支付请求 using var client = new HttpClient(); var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}/pg/v1/pay") { Headers = { { "X-VERIFY", signature } }, Content = new StringContent(JsonSerializer.Serialize(new { request = payload }), Encoding.UTF8, "application/json") }; var response = await client.SendAsync(requestMessage, ct); var responseContent = await response.Content.ReadAsStringAsync(ct); var result = JsonSerializer.Deserialize<PhonePePaymentResponse>(responseContent); return result.Data.InstrumentResponse.RedirectInfo.Url; // 返回跳转URL给前端 } // SHA256哈希工具方法 private string HashSHA256(string input) { using var sha256 = SHA256.Create(); var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(input)); return BitConverter.ToString(bytes).Replace("-", "").ToLower(); } } // 回调处理接口 [ApiController] [Route("api/payments")] public class PaymentController : ControllerBase { private readonly IConfiguration _config; public PaymentController(IConfiguration config) { _config = config; } [HttpPost("phonepe-callback")] public IActionResult PhonePeCallback([FromBody] PhonePeCallbackRequest callbackRequest) { var saltKey = _config["PhonePeSettings:SaltKey"]; var signature = callbackRequest.Signature; var payload = callbackRequest.Response; // 验证签名 var expectedSignatureRaw = $"{payload}/{callbackRequest.MerchantTransactionId}/{saltKey}"; var expectedSignature = HashSHA256(expectedSignatureRaw) + "###" + _config["PhonePeSettings:SaltIndex"]; if (signature != expectedSignature) return BadRequest("无效签名"); var responseData = JsonSerializer.Deserialize<PhonePeCallbackResponse>(Convert.FromBase64String(payload)); if (responseData.Data.TransactionStatus == "SUCCESS") { // 处理支付成功逻辑(如更新订单状态) return Ok(new { status = "SUCCESS" }); } return BadRequest("支付失败"); } private string HashSHA256(string input) { using var sha256 = SHA256.Create(); var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(input)); return BitConverter.ToString(bytes).Replace("-", "").ToLower(); } } // 辅助模型(需根据PhonePe官方文档定义完整字段) public class PhonePePaymentResponse { /* 省略字段定义 */ } public class PhonePeCallbackRequest { /* 省略字段定义 */ } public class PhonePeCallbackResponse { /* 省略字段定义 */ }
三、Paytm UPI支付集成
Paytm同样提供独立API,需完成订单创建、签名验证、回调处理流程。
1. 配置参数(appsettings.json)
"PaytmSettings": { "MerchantId": "你的商户ID", "MerchantKey": "你的商户密钥", "BaseUrl": "https://securegw.paytm.in", "CallbackUrl": "https://你的公网域名/api/payments/paytm-callback" }
2. 支付发起与回调处理代码
// Paytm支付服务 public class PaytmPaymentService { private readonly IConfiguration _config; public PaytmPaymentService(IConfiguration config) { _config = config; } public async Task<string> InitiatePaymentAsync(string orderId, long amount, string userId, CancellationToken ct) { var merchantId = _config["PaytmSettings:MerchantId"]; var merchantKey = _config["PaytmSettings:MerchantKey"]; var baseUrl = _config["PaytmSettings:BaseUrl"]; var callbackUrl = _config["PaytmSettings:CallbackUrl"]; // 构建请求参数 var parameters = new Dictionary<string, string> { { "MID", merchantId }, { "ORDER_ID", orderId }, { "CUST_ID", userId }, { "TXN_AMOUNT", amount.ToString() }, { "CHANNEL_ID", "WAP" }, { "INDUSTRY_TYPE_ID", "Retail" }, { "WEBSITE", "WEBSTAGING" }, // 生产环境改为DEFAULT { "CALLBACK_URL", callbackUrl }, { "PAYMENT_MODE_ONLY", "UPI" } // 指定仅UPI支付 }; // 生成签名 var signature = GeneratePaytmSignature(parameters, merchantKey); parameters.Add("CHECKSUMHASH", signature); // 创建订单并获取跳转URL using var client = new HttpClient(); var content = new FormUrlEncodedContent(parameters); var response = await client.PostAsync($"{baseUrl}/order/create", content, ct); var responseContent = await response.Content.ReadAsStringAsync(ct); var result = JsonSerializer.Deserialize<PaytmCreateOrderResponse>(responseContent); if (result.Status == "SUCCESS") return $"{baseUrl}/theia/api/v1/showPaymentPage?mid={merchantId}&orderId={orderId}"; throw new Exception("Paytm支付发起失败"); } // Paytm签名生成方法 private string GeneratePaytmSignature(Dictionary<string, string> parameters, string merchantKey) { var sortedParams = parameters.OrderBy(k => k.Key).ToDictionary(k => k.Key, v => v.Value); var stringToHash = string.Join("|", sortedParams.Values) + "|" + merchantKey; using var sha256 = SHA256.Create(); var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(stringToHash)); return Convert.ToBase64String(bytes); } } // 回调处理接口 [HttpPost("paytm-callback")] public IActionResult PaytmCallback([FromForm] Dictionary<string, string> formData) { var merchantKey = _config["PaytmSettings:MerchantKey"]; var signature = formData["CHECKSUMHASH"]; formData.Remove("CHECKSUMHASH"); // 验证签名 var computedSignature = GeneratePaytmSignature(formData, merchantKey); if (computedSignature != signature) return BadRequest("无效签名"); if (formData["STATUS"] == "TXN_SUCCESS") { // 处理支付成功逻辑 return Ok(new { status = "SUCCESS" }); } return BadRequest("支付失败"); } // 辅助模型(需根据Paytm官方文档定义完整字段) public class PaytmCreateOrderResponse { /* 省略字段定义 */ }
注意事项
- 所有支付网关需提前申请商户账号,获取对应的密钥、商户ID等参数
- 金额需转换为对应货币的最小单位(如印度卢比=派萨,1卢比=100派萨)
- 回调地址需为公网可访问地址,测试阶段可使用ngrok等工具映射本地端口
- 需完善异常处理与日志记录,避免直接返回null
内容的提问来源于stack exchange,提问作者rameshsriram
相关产品推荐
相关产品推荐

