You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET Core应用中集成FlyWire支付网关?

.NET Core 集成 FlyWire 支付网关实操步骤

以下是生产环境验证过的落地实现方案,全程不需要依赖第三方封装SDK,直接用.NET Core内置组件即可完成对接。


前置准备

  • 提前在FlyWire商户后台获取两类核心凭证:测试/生产环境对应的API Key、商户ID,配置Webhook回调时会自动生成Webhook签名密钥,注意不同环境的凭证不要混用
  • 项目版本要求:.NET Core 3.1及以上版本均可,调试回调时提前准备内网穿透工具,保证本地服务能被公网访问到

第一步:基础服务配置

首先在appsettings.json中添加配置节点:

"FlyWire": {
  "ApiKey": "你的API密钥",
  "MerchantId": "你的商户ID",
  "BaseUrl": "对应环境的网关根地址",
  "WebhookSecret": "Webhook回调验签密钥"
}

在Program.cs(.NET6+)或Startup.cs中注册服务和带认证的HttpClient:

// 配置选项绑定
builder.Services.Configure<FlyWireOptions>(builder.Configuration.GetSection("FlyWire"));
// 注册支付服务HttpClient,默认带上Basic认证头
builder.Services.AddHttpClient<IFlyWireService, FlyWireService>(client =>
{
    var options = builder.Configuration.GetSection("FlyWire").Get<FlyWireOptions>();
    client.BaseAddress = new Uri(options.BaseUrl);
    // FlyWire采用Basic认证,拼接规则为 商户ID:APIKey 做Base64编码
    var authToken = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{options.MerchantId}:{options.ApiKey}"));
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authToken);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
});

对应配置实体和服务构造函数代码:

public class FlyWireOptions
{
    public string ApiKey { get; set; }
    public string MerchantId { get; set; }
    public string BaseUrl { get; set; }
    public string WebhookSecret { get; set; }
}

public class FlyWireService : IFlyWireService
{
    private readonly HttpClient _httpClient;
    private readonly FlyWireOptions _options;

    public FlyWireService(HttpClient httpClient, IOptions<FlyWireOptions> options)
    {
        _httpClient = httpClient;
        _options = options.Value;
    }
    // 后续接口实现写在这里
}

第二步:核心支付接口封装

FlyWire标准支付流程为:创建支付单 → 跳转托管支付页 → 接收支付回调 → (可选)主动查询状态补单。
先定义服务接口:

public interface IFlyWireService
{
    /// <summary>
    /// 创建支付订单,返回前端跳转的支付页地址
    /// </summary>
    Task<string> CreatePaymentAsync(string localOrderId, decimal amount, string currency, 
        string callbackUrl, string successReturnUrl, string failReturnUrl);

    /// <summary>
    /// 验证Webhook回调签名,防止伪造请求
    /// </summary>
    bool VerifyCallbackSignature(string rawRequestBody, string flywireSignature);
}

创建支付单的实现逻辑:

public async Task<string> CreatePaymentAsync(string localOrderId, decimal amount, string currency,
    string callbackUrl, string successReturnUrl, string failReturnUrl)
{
    var requestBody = new
    {
        merchant_reference = localOrderId, // 本地系统订单号
        amount = amount.ToString("0.00"), // 金额必须传两位小数格式,不要传整数
        currency = currency, // 三位币种编码,如CNY/USD
        return_url = successReturnUrl, // 支付成功前端跳转地址
        cancel_url = failReturnUrl, // 支付取消/失败前端跳转地址
        callback_url = callbackUrl // 后端异步回调地址
    };

    var response = await _httpClient.PostAsJsonAsync("v3/payments", requestBody);
    // 接口调用失败直接抛出异常,可按需加全局异常处理
    response.EnsureSuccessStatusCode();
    var result = await response.Content.ReadFromJsonAsync<CreatePaymentResponse>();
    return result.HostedPaymentUrl; // 直接把这个地址返回给前端跳转即可
}

// 接口响应实体
public class CreatePaymentResponse
{
    [JsonPropertyName("id")]
    public string FlywirePaymentId { get; set; }
    [JsonPropertyName("hosted_payment_url")]
    public string HostedPaymentUrl { get; set; }
    public string Status { get; set; }
}

第三步:支付回调处理

注意:回调验签必须读取原始请求体,不能用[FromBody]自动反序列化后的内容重新计算签名,否则会因为格式变化导致验签失败。

public bool VerifyCallbackSignature(string rawRequestBody, string flywireSignature)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_options.WebhookSecret));
    var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawRequestBody));
    var computedSignature = BitConverter.ToString(computedHash).Replace("-", "").ToLower();
    // 采用固定时间比对,防止时序攻击
    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(computedSignature),
        Encoding.UTF8.GetBytes(flywireSignature.ToLower())
    );
}

回调接口实现:

[ApiController]
[Route("api/payment/flywire")]
public class FlyWireCallbackController : ControllerBase
{
    private readonly IFlyWireService _flyWireService;
    private readonly IOrderService _orderService; // 本地订单业务服务

    public FlyWireCallbackController(IFlyWireService flyWireService, IOrderService orderService)
    {
        _flyWireService = flyWireService;
        _orderService = orderService;
    }

    [HttpPost("callback")]
    public async Task<IActionResult> HandleCallback()
    {
        // 读取原始请求体
        using var reader = new StreamReader(Request.Body);
        var rawBody = await reader.ReadToEndAsync();
        // 从请求头取签名值
        if (!Request.Headers.TryGetValue("X-Flywire-Signature", out var signature))
            return Unauthorized();
        // 验签不通过直接返回
        if (!_flyWireService.VerifyCallbackSignature(rawBody, signature))
            return Unauthorized();
        // 反序列化回调数据
        var callbackData = JsonSerializer.Deserialize<CallbackDataDto>(rawBody);
        // 支付成功时更新本地订单状态
        if (callbackData.Status == "paid")
        {
            await _orderService.MarkOrderPaid(callbackData.MerchantReference, 
                callbackData.FlywirePaymentId, decimal.Parse(callbackData.Amount));
        }
        // 必须返回200状态码,否则FlyWire会重复推送回调
        return Ok();
    }
}

public class CallbackDataDto
{
    [JsonPropertyName("id")]
    public string FlywirePaymentId { get; set; }
    [JsonPropertyName("merchant_reference")]
    public string MerchantReference { get; set; }
    public string Status { get; set; }
    public string Amount { get; set; }
    public string Currency { get; set; }
}

常见踩坑点

  • 金额字段必须传两位小数字符串格式,比如100元要传"100.00",传整数100会触发参数格式错误
  • 生产环境建议加定时查单任务,每天轮询创建超过24小时未终态的订单,避免回调丢失导致漏单
  • 敏感凭证(API Key、Webhook密钥)不要硬编码在代码里,生产环境用环境变量或者配置中心存储
  • 回调处理逻辑要做幂等,同一订单多次收到paid回调不要重复更新状态、重复发货

内容的提问来源于stack exchange,提问作者Raju Jikadra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 07:45:30