PayPal JavaScript SDK使用服务器端REST调用时是否支持授权意图?
我正在尝试使用JavaScript SDK和服务器端REST调用的组合在我们的购物车中实施PayPal付款。我使用JavaScript SDK渲染按钮,并具有create -codeord
和onapprove
方法在我的服务器上调用端点,如PayPal do所示的示例( javascript SDK示例)。这些反过来又致电PayPal REST API。
如果我要捕获,所有这些都很好。但是,我们的要求是执行授权。这似乎不起作用。 createorder
成功完成呼叫,onapprove
进行命中。但是,当服务器侧rest呼叫发生错误时,它会失败:
essue':“ Munter_cannot_be_specified”,“ Description”:“只有通过拨打/v2/v2/checkout/orders/{order_id}/保存保存订单时才能指定授权金额。请保存订单,然后重试。”}],”
没有提到我必须调用此保存
方法,实际上,如果我尝试调用该方法,我会收到一个错误表明该交易尚未获得客户的批准。
当使用纯客户端的方法时,这也可以正常工作,但是由于多种原因,我们想避免这种情况。 PayPal文档似乎表明这是可能的。
我的代码的简化/消毒版本如下:
<script src="https://www.paypal.com/sdk/js?client-id=KEY¤cy=USD&disable-funding=card&components=funding-eligibility,buttons&intent=authorize&commit=true"></script>
<script>
paypal.Buttons({
createOrder: function (data, actions) {
return fetch("/checkout/paypal/order", {
method: "post",
body: $("#checkoutForm").serialize(),
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
}).then((order) => {
return order.json();
})
.then((orderData) => {
return orderData.OrderId;
});
},
onApprove: function (data, actions) {
return fetch("/checkout/paypal/authorize", {
method: "post",
body: $('#checkoutForm').serialize(),
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
})
.then((result) => {
console.log(result.json());
});
}
}).render('#paypal-button-container');
</script>
后端通过控制器和许多服务,但是最终由此产生的请求(使用C#SDK软件包:PayPalCheckEckoutsDK)看起来像这样:
创建订单:
public async Task<PayPalOrderResponseDTO> CreateOrder(PayPalOrderRequestDTO orderRequest)
{
OrderRequest ppor = new OrderRequest()
{
CheckoutPaymentIntent = "AUTHORIZE",
PurchaseUnits = new List<PurchaseUnitRequest>()
{
new PurchaseUnitRequest()
{
AmountWithBreakdown = new AmountWithBreakdown()
{
CurrencyCode = "USD",
Value = orderRequest.Total.ToString()
},
ShippingDetail = new ShippingDetail()
{
Name = new Name()
{
FullName = $"{orderRequest.ShippingAddress.FirstName} {orderRequest.ShippingAddress.LastName}"
},
AddressPortable = new AddressPortable()
{
AddressLine1 = orderRequest.ShippingAddress.Address1,
AddressLine2 = orderRequest.ShippingAddress.Address2,
AddressLine3 = orderRequest.ShippingAddress.Address3,
AdminArea2 = orderRequest.ShippingAddress.City,
AdminArea1 = orderRequest.ShippingAddress.State,
PostalCode = orderRequest.ShippingAddress.ZipCode,
CountryCode = orderRequest.ShippingAddress.CountryID
}
}
}
},
ApplicationContext = new ApplicationContext()
{
ShippingPreference = "SET_PROVIDED_ADDRESS",
LandingPage = "LOGIN",
UserAction = "PAY_NOW"
}
};
OrdersCreateRequest request = new OrdersCreateRequest();
request.Prefer("return=minimal");
request.RequestBody(ppor);
PayPalHttp.HttpResponse response = await _ppClient.Execute(request).ConfigureAwait(false);
System.Net.HttpStatusCode statusCode = response.StatusCode;
if (statusCode != System.Net.HttpStatusCode.Created)
{
// HANDLE ERROR
}
Order order = response.Result<Order>();
return new PayPalOrderResponseDTO()
{
Status = response.StatusCode.ToString(),
OrderID = order.Id
};
}
授权订单:
public async Task<PayPalPaymentResponseDTO> Authorize(PayPalPaymentRequestDTO request)
{
OrdersAuthorizeRequest oar = new OrdersAuthorizeRequest(request.OrderID);
oar.RequestBody(new AuthorizeRequest()
{
Amount = new Money() { CurrencyCode = "USD", Value = request.Total },
ReferenceId = request.refId
});
PayPalHttp.HttpResponse response = await _ppClient.Execute(oar).ConfigureAwait(false);
Order order = response.Result<Order>();
return new PayPalPaymentResponseDTO()
{
StatusCode = (int)response.StatusCode,
ID = order.Id
};
}
正如我说的那样,如果我将其更改为使用“捕获”意图,这一切都可以很好地工作。只有当我尝试“授权”时,我才会遇到此错误。我尝试在没有金额的情况下进行最终授权电话,以防万一,但出现了一个错误,表明所需的付款金额缺少。
有人有任何想法吗?还是在不进行较旧式的重定向的情况下这是不可能的?我想避免这两个,并使用纯客户端的方法来处理此操作,例如:
paypal.Buttons({
// Sets up the transaction when a payment button is clicked
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [{
amount: {
value: '77.44' // Can also reference a variable or function
}
}]
});
},
// Finalize the transaction after payer approval
onApprove: (data, actions) => {
return actions.order.capture().then(function(orderData) {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
actions.redirect('thank_you.html');
});
}
}).render('#paypal-button-container');
I'm trying to implement PayPal payments in our shopping cart using a combination of the JavaScript SDK and server side REST calls. I render the buttons using the JavaScript SDK and have the createOrder
and onApprove
methods call endpoints on my server, as the examples given by PayPal do (JavaScript SDK example). These, in turn, call to the PayPal REST API.
All of this works great IF I'm doing a capture. However, our requirements are to do an authorization. This doesn't appear to work. The createOrder
call successfully completes and the onApprove
method is hit. However it fails when the server side REST call is made with an error:
issue":"AMOUNT_CANNOT_BE_SPECIFIED","description":"An authorization amount can only be specified if an Order has been saved by calling /v2/checkout/orders/{order_id}/save. Please save the order and try again."}],"
There is no mention that I can find of having to call this save
method and indeed, if I try to call that method, I get an error indicating the transaction was not yet approved by the customer.
This also works fine when using the pure client side method of doing this, but we would like to avoid that for a number of reasons. The PayPal documentation seems to indicate that this should be possible.
A simplified/sanitized version of my code is below:
<script src="https://www.paypal.com/sdk/js?client-id=KEY¤cy=USD&disable-funding=card&components=funding-eligibility,buttons&intent=authorize&commit=true"></script>
<script>
paypal.Buttons({
createOrder: function (data, actions) {
return fetch("/checkout/paypal/order", {
method: "post",
body: $("#checkoutForm").serialize(),
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
}).then((order) => {
return order.json();
})
.then((orderData) => {
return orderData.OrderId;
});
},
onApprove: function (data, actions) {
return fetch("/checkout/paypal/authorize", {
method: "post",
body: $('#checkoutForm').serialize(),
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
})
.then((result) => {
console.log(result.json());
});
}
}).render('#paypal-button-container');
</script>
The backend goes through a controller and a number of services, but ultimately the resulting requests (using the C# SDK package: PayPalCheckoutSdk) looks like this:
Create Order:
public async Task<PayPalOrderResponseDTO> CreateOrder(PayPalOrderRequestDTO orderRequest)
{
OrderRequest ppor = new OrderRequest()
{
CheckoutPaymentIntent = "AUTHORIZE",
PurchaseUnits = new List<PurchaseUnitRequest>()
{
new PurchaseUnitRequest()
{
AmountWithBreakdown = new AmountWithBreakdown()
{
CurrencyCode = "USD",
Value = orderRequest.Total.ToString()
},
ShippingDetail = new ShippingDetail()
{
Name = new Name()
{
FullName = quot;{orderRequest.ShippingAddress.FirstName} {orderRequest.ShippingAddress.LastName}"
},
AddressPortable = new AddressPortable()
{
AddressLine1 = orderRequest.ShippingAddress.Address1,
AddressLine2 = orderRequest.ShippingAddress.Address2,
AddressLine3 = orderRequest.ShippingAddress.Address3,
AdminArea2 = orderRequest.ShippingAddress.City,
AdminArea1 = orderRequest.ShippingAddress.State,
PostalCode = orderRequest.ShippingAddress.ZipCode,
CountryCode = orderRequest.ShippingAddress.CountryID
}
}
}
},
ApplicationContext = new ApplicationContext()
{
ShippingPreference = "SET_PROVIDED_ADDRESS",
LandingPage = "LOGIN",
UserAction = "PAY_NOW"
}
};
OrdersCreateRequest request = new OrdersCreateRequest();
request.Prefer("return=minimal");
request.RequestBody(ppor);
PayPalHttp.HttpResponse response = await _ppClient.Execute(request).ConfigureAwait(false);
System.Net.HttpStatusCode statusCode = response.StatusCode;
if (statusCode != System.Net.HttpStatusCode.Created)
{
// HANDLE ERROR
}
Order order = response.Result<Order>();
return new PayPalOrderResponseDTO()
{
Status = response.StatusCode.ToString(),
OrderID = order.Id
};
}
Authorize order:
public async Task<PayPalPaymentResponseDTO> Authorize(PayPalPaymentRequestDTO request)
{
OrdersAuthorizeRequest oar = new OrdersAuthorizeRequest(request.OrderID);
oar.RequestBody(new AuthorizeRequest()
{
Amount = new Money() { CurrencyCode = "USD", Value = request.Total },
ReferenceId = request.refId
});
PayPalHttp.HttpResponse response = await _ppClient.Execute(oar).ConfigureAwait(false);
Order order = response.Result<Order>();
return new PayPalPaymentResponseDTO()
{
StatusCode = (int)response.StatusCode,
ID = order.Id
};
}
As I said, this all works perfectly if I change it to use a "CAPTURE" intent. It's only when I attempt a "AUTHORIZE" that I get this error. I tried doing the final authorization call without the amount, just in case, but got an error indicating that the required payment amount field was missing.
Does any one have any ideas, or is this just not possible without doing an older-style redirect? I'd like to avoid both that and using the purely client side method of handling this using something like, e.g.:
paypal.Buttons({
// Sets up the transaction when a payment button is clicked
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [{
amount: {
value: '77.44' // Can also reference a variable or function
}
}]
});
},
// Finalize the transaction after payer approval
onApprove: (data, actions) => {
return actions.order.capture().then(function(orderData) {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
actions.redirect('thank_you.html');
});
}
}).render('#paypal-button-container');
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
可能。授权该订单的REST API呼吁应为无有效载荷主体的帖子。
这是一个示例日志,由服务器上的createorder触发:
随后在服务器上触发,在onapprove之后触发:
一个示例以使用结帐net-sdk 在这里。
It is possible. The REST API call to authorize the order should be a POST with no payload body.
Here is an example log, triggered by createOrder on the server:
Followed by this on the server, triggered after onApprove:
A sample to do this with the Checkout-NET-SDK is here.