我在尝试使用更新的总计修补 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=='默认'/金额”
我尝试使用将值设置为的组合:
当使用指定为值的 AmountWithBreakdown 对象调用 API 时,我遇到了 .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 实现所有其他功能的要点。
System.Runtime.Serialization.SerializationException:不应输入数据合约名称为“AmountWithBreakdown:
http://schemas.datacontract.org/2004/07/PayPalCheckoutSdk.Orders”的“PayPalCheckoutSdk.Orders.AmountWithBreakdown”。如果您使用 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;
}
}
public async Task<UpdatePayPalV2CheckoutPaymentResponse> UpdatePayment(string orderId, decimal amount)
{
try
{
var request = new OrdersPatchRequest<object>(orderId);
// This doesn't work because 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());
}