【问题标题】:Issue Patching Order Amount with Breakdown - Paypal Checkout NET SDK发出带有细分的修补订单金额 - Paypal Checkout NET SDK
【发布时间】:2021-10-25 20:29:41
【问题描述】:

我在尝试用更新的总数修补 PayPal 订单时遇到了麻烦。我正在使用他们在 GitHub 上提供的 PayPal Checkout-NET-SDK,但他们为 Patch Order Sample 提供的示例文档有点过于简单: https://github.com/paypal/Checkout-NET-SDK/blob/develop/Samples/PatchOrderSample.cs

我正在尝试更新以下路径: /purchase_units/@reference_id=='default'/amount"

我尝试过将值设置为:

  • 表示 AmountWithBreakdown 对象的 JSON 字符串
  • AmountWithBreakdown 对象

当调用 API 并分配一个 AmountWithBreakdown 对象作为值时,我遇到了一个 .NET 异常:

不应使用数据合同名称“AmountWithBreakdown:http://schemas.datacontract.org/2004/07/PayPalCheckoutSdk.Orders'”键入“PayPalCheckoutSdk.Orders.AmountWithBreakdown”。如果您正在使用 DataContractSerializer 或将任何静态未知的类型添加到已知类型列表中,请考虑使用 DataContractResolver - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给序列化程序的已知类型列表中。

构建 PATCH 请求的示例函数:

 Private Function BuildPatchRequest() As List(Of Patch(Of Object))

        Dim patches = New List(Of Patch(Of Object)) From {
            New Patch(Of Object) With {
                .Op = "replace",
                .Path = "/intent",
                .Value = "CAPTURE"
            },
            New Patch(Of Object) With {
                .Op = "replace",
                .Path = "/purchase_units/@reference_id=='default'/amount",
                .Value = New AmountWithBreakdown With {
                            .CurrencyCode = Me.Order.CurrencyCode,
                            .Value = Me.Order.Total.ToString("N2"),
                            .AmountBreakdown = New AmountBreakdown With {
                                .ItemTotal = New PayPalCheckoutSdk.Orders.Money With {.CurrencyCode = Me.Order.CurrencyCode, .Value = Me.Order.SubTotal.ToString("N2")},
                                .TaxTotal = New PayPalCheckoutSdk.Orders.Money With {.CurrencyCode = Me.Order.CurrencyCode, .Value = Me.Order.TaxTotal.ToString("N2")},
                                .Shipping = New PayPalCheckoutSdk.Orders.Money With {.CurrencyCode = Me.Order.CurrencyCode, .Value = Me.Order.ShippingTotal.ToString("N2")},
                                .Discount = New PayPalCheckoutSdk.Orders.Money With {.CurrencyCode = Me.Order.CurrencyCode, .Value = Me.Order.DiscountTotal.ToString("N2")},
                                .Handling = New PayPalCheckoutSdk.Orders.Money With {.CurrencyCode = Me.Order.CurrencyCode, .Value = Me.Order.HandlingFeeTotal.ToString("N2")},
                                .Insurance = New PayPalCheckoutSdk.Orders.Money With {.CurrencyCode = Me.Order.CurrencyCode, .Value = "0.00"},
                                .ShippingDiscount = New PayPalCheckoutSdk.Orders.Money With {.CurrencyCode = Me.Order.CurrencyCode, .Value = "0.00"}
                            }
                        }
            }
        }
        Return patches

End Function

尽管输出通过了 JSON 验证工具,但所有将 JSON 手动构造为字符串并将其分配给值的尝试都会遇到通用 INVALID_PARAMETER_SYNTAX 错误响应。

有没有人使用此 SDK 通过 PayPal 成功更新此数据点?我的实现是在 VB 中,但我已经掌握了使用 C# 中的 SDK 实现所有其他功能的要点。

【问题讨论】:

  • “尽管输出通过了 JSON 验证工具,但手动将 JSON 构造为字符串并将其分配给值的所有尝试都会遇到通用的 INVALID_PARAMETER_SYNTAX 错误响应。”记录这些尝试的原始请求+响应正文,以便我们查看它们的问题所在。
  • Preston PHX,我尝试修补您原始评论中的各个点,但在回复中遇到了 UNPATCHABLE 评论。我需要下载完整的项目源代码并与我的项目集成,以便捕获原始请求——我现在只是使用 SDK 中提供的类取回一个对象。
  • 所以我查看了 PayPalHttp-DotNet 依赖项的底层,它实现了正在构建的 HttpRequest 对象的 Execute 方法,看起来它正在序列化对象——所以我假设 JSON我传递的字符串只是被解析为常规字符串,作为补丁请求对象中的值参数。参考:github.com/paypal/paypalhttp_dotnet/blob/master/…
  • 这是有道理的,但我们需要查看您的最佳测试的完整请求和响应,以便就可能出现的问题提出建议

标签: paypal sdk patch checkout orders


【解决方案1】:

我遇到了同样的问题。

System.Runtime.Serialization.SerializationException:键入“PayPalCheckoutSdk.Orders.AmountWithBreakdown”,数据合同名称为“AmountWithBreakdown:http://schemas.datacontract.org/2004/07/PayPalCheckoutSdk.Orders'”。如果您使用 DataContractSerializer 或将任何静态未知的类型添加到已知类型列表中,请考虑使用 DataContractResolver - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给序列化程序的已知类型列表中。

该调用从未向 PayPal 发送响应,因为它无法序列化请求。我能够让它工作的唯一方法是在没有 SDK 的情况下生成我自己的请求。这不是生产就绪代码。我只是在测试如果我自己制定请求是否会起作用,并且确实如此。

var ppe = PayPalClient.environment(productLine);

        var accessTokenClient = new HttpClient(ppe);
        var accessTokenRequest = new AccessTokenRequest(ppe, null);

        var accessTokenResponse = Task.Run(async () =>
        {
            var httpResponse = await accessTokenClient.Execute(accessTokenRequest);
            return httpResponse;
        }).Result;

        if (accessTokenResponse.StatusCode != HttpStatusCode.OK)
        {
            Logger.Error("[PayPalRestService] Unable to get access token.");
            return null;
        }
        
        var accessToken = accessTokenResponse.Result<AccessToken>();

        var client = new RestClient(ppe.BaseUrl());
        client.AddDefaultHeader("Authorization", $"Bearer {accessToken.Token}");


        var body = BuildUpdateTaxPatchRequest(cart, paypalOrder);
        var request = new RestRequest($"/v2/checkout/orders/{paypalOrder.Id}")
            .AddJsonBody(JsonConvert.SerializeObject(body));

        var response = client.Patch(request);

        if (response.StatusCode == HttpStatusCode.NoContent)
        {
            return GetOrder(paypalOrder.Id, productLine);
        }




private static List<Patch<object>> BuildUpdateTaxPatchRequest(ICart cart, Order paypalOrder)
    {
        //Doc: https://developer.paypal.com/docs/api/orders/v2/#orders_patch

        var subtotal = $"{cart.GetSubTotal():N2}";
        var shipping = $"{cart.GetShippingTotal():N2}";
        var discount = $"{cart.GetOrderDiscountTotal():N2}";
        var tax = $"{cart.GetTaxTotal():N2}";
        var total = double.Parse(tax, CultureInfo.GetCultureInfo("en-US")) + double.Parse(shipping, CultureInfo.GetCultureInfo("en-US")) + double.Parse(subtotal, CultureInfo.GetCultureInfo("en-US")) - double.Parse(discount, CultureInfo.GetCultureInfo("en-US"));
        var strTotal = total.ToString("0.00", CultureInfo.GetCultureInfo("en-US"));


        var updates = new AmountWithBreakdown()
        {
            CurrencyCode = paypalOrder.PurchaseUnits.First().AmountWithBreakdown.CurrencyCode,
            Value = strTotal,
            AmountBreakdown = new AmountBreakdown
            {
                ItemTotal = new Money
                {
                    CurrencyCode = cart.Currency.CurrencyCode,
                    Value = subtotal
                },
                Shipping = new Money
                {
                    CurrencyCode = cart.Currency.CurrencyCode,
                    Value = shipping
                },
                TaxTotal = new Money
                {
                    CurrencyCode = cart.Currency.CurrencyCode,
                    Value = tax
                },
            }
        };

        var patches = new List<Patch<object>>
        {
            new Patch<object>
            {
                Op= "replace",
                Path= "/purchase_units/@reference_id=='default'/amount",
                Value = updates
            }
        };

        return patches;
    }
}

【讨论】:

  • Scott,谢谢你——作为一个绝对的后备,我可能不得不走这条路。我有一个开放的 PayPal 支持案例,看看他们是否不能让 SDK 项目的开发人员之一来调查这个问题。他们的 GitHub 页面没有问题部分,他们将您引导至 PayPal 支持,所以我希望我能在那里得到一些答案。
  • 我已经要求 PayPal 的支持案例将其传递给他们的 SDK 开发团队,并引用了这个问题的 URL。斯科特,将您的答案标记为正确,因为我能够使用 .NET HttpWebRequest 对象实现类似的方法,并对请求正文返回的对象进行自序列化。如果我听到他们已经解决了这个问题或解决了如何纯粹使用他们的 SDK 来解决这个问题,我会再次更新。
【解决方案2】:

我也遇到了这个问题,我查看了 PayPalHttp.HttpClient 源代码并找到了适合我的解决方案。也许这对你有帮助。

public async Task<UpdatePayPalV2CheckoutPaymentResponse> UpdatePayment(string orderId, decimal amount)
{
    try
    {
        var request = new OrdersPatchRequest<object>(orderId);

        // This doesn't work bacause DataContractSerializer.
        // request.RequestBody(BuildPatchRequest(amount));

        var json = JsonConvert.SerializeObject(BuildPatchRequest(amount));
        request.Content = new StringContent(json, Encoding.UTF8, "application/json");

        await CreatePayPalClient().Execute(request);
    }
    catch (Exception e)
    {
        return new UpdatePayPalV2CheckoutPaymentResponse
        {
            Success = false,
            Message = e.Message
        };
    }

    return new UpdatePayPalV2CheckoutPaymentResponse
    {
        Success = true
    };
}

private static List<Patch<object>> BuildPatchRequest(decimal amount)
{
    var patches = new List<Patch<object>>
    {
        new Patch<object>
        {
            Op = "replace",
            Path = "/purchase_units/@reference_id=='default'/amount",
            Value = new AmountWithBreakdown
            {
                CurrencyCode = "USD",
                Value = amount.ToString("F")
            }
        }
    };

    return patches;
}

private PayPalEnvironment CreatePayPalEnvironment()
{
    PayPalEnvironment environment;
    if (_orderProcessingConfiguration.PayPalCheckoutConfiguration.Sandbox)
    {
        environment = new SandboxEnvironment(
            _orderProcessingConfiguration.PayPalCheckoutConfiguration.ClientId,
            _orderProcessingConfiguration.PayPalCheckoutConfiguration.ClientSecret
            );
    }
    else
    {
        environment = new LiveEnvironment(
            _orderProcessingConfiguration.PayPalCheckoutConfiguration.ClientId,
            _orderProcessingConfiguration.PayPalCheckoutConfiguration.ClientSecret
            );
    }

    return environment;
}

private PayPalHttp.HttpClient CreatePayPalClient()
{
    return new PayPalHttpClient(CreatePayPalEnvironment());
}

【讨论】:

    猜你喜欢
    • 2021-11-12
    • 2018-09-05
    • 2023-04-04
    • 2020-11-09
    • 2015-02-05
    • 2011-11-30
    • 2014-07-29
    • 2012-11-29
    • 2013-10-20
    相关资源
    最近更新 更多