i'm using Paypal PHP SDK (in Laravel) to prepare a payment. At the moment, that payment looks like this:
{
"intent":"sale",
"payer":{
"payment_method":"paypal"
},
"redirect_urls":{
"return_url":"http://website.lab/payment/paypal/success",
"cancel_url":"http://website.lab/payment/paypal/cancel"
},
"transactions":[
{
"amount":{
"currency":"EUR",
"total":"223",
"details":{
"shipping":"0",
"subtotal":"273"
}
},
"description":"...",
"item_list":{
"items":[
{
"name":"product 8",
"currency":"EUR",
"quantity":21,
"sku":"w9",
"price":"13.00"
}
],
"shipping_address":{
"city":"...",
"postal_code":"...",
"country_code":"...",
"recipient_name":"...",
"line1":"..."
}
}
}
]
}
As you can see there are 50€ missing from the total to the subtotal and this cause this error
400{"name":"VALIDATION_ERROR","details":[{"field":"transactions[0].amount","issue":"Transaction amount details (subtotal, tax, shipping) must add up to specified amount total"}],"message":"Invalid request - see details","information_link":"https://developer.paypal.com/docs/api/payments/#errors","debug_id":"91d27597e954"}
Now looking to the Paypal SDK i'm not able to find any sort of method/Object that can tell Paypal to add a discount to the subtotal.
Any help will be appreciate, i really can't find any resource on the web that can solve this problem
You can add an item to your "item_list" with a negative value to the transaction:
{
"name": "Discount",
"price": "-50",
"currency": "EUR",
"quantity": "1"
}
Related
I have been trying to build a custom Woocommerce Cart API endpoint, I implemented the add to cart feature but I have ended up facing an issue with shipping taxes.
I want to calculate shipping taxes (especially for flexible_shipping_single) but my code isn't working :
$shippings = [];
if (isset($request["shipping_lines"]) && !empty($request["shipping_lines"])) {
foreach ($request["shipping_lines"] as $shipping_line) {
$shippings[] = $shipping_line["method_id"];
}
WC()->session->set('chosen_shipping_methods', $shippings);
}
$cart->calculate_totals();
Here's my request (I removed some lines to minimize the size)
{
"line_items": [
{
"product_id": 15071,
"variation_id": 0,
"quantity": 9
}
],
"shipping": {
"first_name": "John",
"last_name": "Doe",
"address_1": "969 Market",
"address_2": "",
"city": "San Francisco",
"state": "CA",
"postcode": "94103",
"country": "US"
},
"shipping_lines": [
{
"method_id": "flexible_shipping_single",
"method_title": "Flexible Shipping"
}
]
}
The issue:
The issue was in the param "state": CA that doesn't exist in the state's list of the shipping zones, So the calculate_shipping() couldn't find the shipping package related to the shipping method selected by the user and its rates, which means that the flexible_shipping_single won't work, instead, it sets back the default shipping method, in my case wc_pickup_store
I'm new with STRIPE, and I've been reading the documentation for STRIPE, and was task to create a list of payouts to a connected account (type is standard). Also, I have to show the details, under those PAYOUT, all the payments included in it.
However I can't see any relation to PAYOUTS with PAYMENT INTENTS/CHARGES, is it possible to know all those payments included in the PAYOUTS ? We are creating STANDARD connect accounts for our users.
I had the same challenge, and had to go thru 4 supporters, tell my needs over and over again, before i finally got the right hint and could complete my checks and cron jobs. I provide my solution here to save others the same experience:
$po = 'po_sadfahk.....'; // payout id (i do a loop of payouts)
\Stripe\Stripe::setApiKey($stripeSecretKey);
$balanceTransactions = \Stripe\BalanceTransaction::all([
'payout' => "$po",
'type' => 'charge',
'limit' => 100, // default is 10, but a payout can have more pi's
'expand' => ['data.source'],
]);
foreach ($balanceTransactions->data as $txn) {
// my invoice id is added in description when creating the payment intent
echo "Invoice: {$txn->description}\n";
echo "Created: {$txn->created}\n";
echo "Available: {$txn->available_on}\n";
// in source we find the pi_id, amount and currency for each payment intent
$charge = $txn->source;
echo "pi: {$charge->payment_intent}\n";
$amount = $charge->amount/100;
echo "$amount {$charge->currency}\n";
}
Output (cropped to relevant data):
{
"object": "list",
"data": [
{
"id": "txn_1ISOaSGqFEoKRtad...",
"object": "balance_transaction",
"amount": 25000,
"available_on": 1615680000,
"created": 1615131127,
"currency": "dkk",
"description": "Invoice 44",
...
"fee": 530,
"fee_details": [
{
"amount": 530,
"application": null,
"currency": "dkk",
"description": "Stripe processing fees",
"type": "stripe_fee"
}
],
...
"source": {
"id": "ch_1ISOaRGqFEoKR...",
"object": "charge",
"amount": 25000,
...
"paid": true,
"payment_intent": "pi_1ISOa3GqFE...", // here we go!
"payment_method": "pm_1ISOaRGqFE...",
"payment_method_details": {
"card": {
"brand": "visa",
...
},
"type": "card"
},
...
},
"status": "available",
"type": "charge"
}
],
"has_more": false,
"url": "/v1/balance_transactions"
}
Kudos to Kim for his answer. I also went through a number of supporters in Stripe to no avail. His post sorted me out.
The code below is a mere Python 3.10 translation from Kim's code above
po = 'po_<some_payment_id>'
balanceTransactions = stripe.BalanceTransaction.list(
payout=po,
type='charge',
limit=100,
expand=['data.source']
)
for txn in balanceTransactions.data:
print("Invoice: {0}".format(txn.description))
print("Created: {0}".format(txn.created))
print("Available: {0}".format(txn.available_on))
charge = txn.source
print("pi: {0}".format(charge.payment_intent))
amount = charge.amount/100
print("{0} {1}".format(amount, charge.currency))
I'm not sure you'll have that access for a Standard Account, but if it you do, you'll be able to list all of the Balance Transactions associated with that Payout, which will have a reference to the Payment Intent in the source field for any Payment Intents.
My sandbox account return this error when creating a payout
{
"name": "PAYOUT_NOT_AVAILABLE",
"message": "You live in a country that is not allowed to send this payout.",
}
This error is not listed in https://developer.paypal.com/docs/api/payments.payouts-batch/#errors
The account is configured in México (MX) and the official documentation includes MX in the payouts docs.
Am I missing something? Can't find any doc that excludes specific countries.
Just some exceptions for Argentina, Brazil and Malaysia.
It also defiens Mexican currency in the features
https://developer.paypal.com/docs/payouts/#payouts-features
I happened to find a similar endpoint but the weird thing is that it's not listed in the main APIs menu but it seems to be working, for reference check:
https://developer.paypal.com/docs/payouts/test
Update: it seems to be related to the test data that's used in the note field, even if using batch payouts the following changed the result status:
{
"sender_batch_header": {
"sender_batch_id": "Payouts_2018_100007",
"email_subject": "You have a payout!",
"email_message": "You have received a payout! Thanks for using our service!"
},
"items":[
{
"recipient_type": "EMAIL",
"amount": {
"value": "500",
"currency": "MXN"
},
"note": "POSPYO001",
"sender_item_id": "201403140001",
"receiver": "ss36#business.example.com",
"alternate_notification_method": {
"phone": {
"country_code": "52",
"national_number": "4491110560"
}
},
"notification_language": "sp-SP"
}
]
}
A single payout looks like this:
{
"sender_payout_header":
{
"sender_batch_id": "1524086406556",
"email_subject": "This email is related to simulation"
},
"items": [
{
"recipient_type": "EMAIL",
"receiver": "payouts-simulator-receiver#paypal.com",
"note": "POSPYO001",
"sender_item_id": "15240864065560",
"amount":
{
"currency": "USD",
"value": "1.00"
}
}]
}
I was having the same issue and it was because my country(India) name is present in supported countries list of PayPal but i got to know about PayPal Commerce Platform availability where it is not supporting India. Check below link
https://developer.paypal.com/docs/api/reference/country-codes/?mark=countries#paypal-commerce-platform-availability
My work is only on sandbox account of paypal for testing so i simply created new test account of country US.
Possibly your paypal account is from any country listed in above link. Please check country of your main paypal account.
I am developing an e-commerce mobiloe application using magento 2 rest apis only.This is the flow for making the REST API calls for order placement.
1.Create a cart
api -->{{url}}/index.php/rest/V1/carts/mine
This api will return a unique cart id
2.Add products to cart
api --> {{url}}/index.php/rest/V1/carts/mine/items
body ->
{
"cart_item": {
"quote_id": cartId,
"sku": skuName,
"qty": 1
}
}
3. Estimate Shipping Methods
url --> {{url}}/index.php/rest/V1/carts/mine/estimate-shipping-methods
body ->
{
"address": {
"region": "Trivandrum",
"region_id": 12,
"region_code": "CA",
"country_id": "IN",
"street": [
"Amstor house",
"Eramam"
],
"telephone": "5656565454",
"postcode": "670390",
"city": "Kazhakuttam",
"firstname": "Peter",
"lastname": "K",
"same_as_billing": 0,
"save_in_address_book": 0
}
}
This will return all possible shipping methods based on shipping address.In my case the result is
[
{
"carrier_code": "freeshipping",
"method_code": "freeshipping",
"carrier_title": "Free Shipping",
"method_title": "Free",
"amount": 0,
"base_amount": 0,
"available": true,
"error_message": "",
"price_excl_tax": 0,
"price_incl_tax": 0
}
]
4)Save shipping information
url --> {{url}}/index.php/rest/V1/carts/mine/shipping-information
body data ->
{
"addressInformation": {
"shipping_address": {
"region": "Trivandrum",
"region_id": 12,
"region_code": "CA",
"country_id": "IN",
"street": [
"Amstor house",
"Eramam"
],
"telephone": "5656565454",
"postcode": "670390",
"city": "Kazhakuttam",
"firstname": "Peter",
"lastname": "K",
},
"billing_address": {
"region": "Trivandrum",
"region_id": 12,
"region_code": "CA",
"country_id": "IN",
"street": [
"Amstor house",
"Eramam"
],
"telephone": "5656565454",
"postcode": "670390",
"city": "Kazhakuttam",
"firstname": "Peter",
"lastname": "K",
},
"shipping_method_code": "freeshipping",
"shipping_carrier_code": "freeshipping"
}
}
This will return all possible payment methods. Here i am using paypal_express for payment.
5. Payment using paypal plugin
Here i will pay the amount using paypal cordova plugin.Also configured the IPN [{{url}}/paypal/ipn/]in paypal account
This api will return the following data,
{
"client": {
"environment": "sandbox",
"paypal_sdk_version": "2.14.4",
"platform": "Android",
"product_name": "PayPal-Android-SDK"
},
"response": {
"create_time": "2016-11-19T05:25:46Z",
"id": "PAY-5VS11410F5341972MLAX6ETA",
"intent": "sale",
"state": "approved"
},
"response_type": "payment"
}
5.Save payment and place order
url --> {{url}}/index.php/rest/V1/carts/mine/payment-information
data ->
{
"cartId": 3,
"billingAddress": {
"region": "Trivandrum",
"region_id": 12,
"region_code": "CA",
"country_id": "IN",
"street": [
"Amstor house",
"Eramam"
],
"telephone": "5656565454",
"postcode": "670390",
"city": "Kazhakuttam",
"firstname": "Peter",
"lastname": "K"
},
"paymentMethod": {
"method": "paypal_express"
}
}
But this api will returning
{
"message": "PayPal gateway has rejected request. Invalid token (#10410: Invalid token)."
}
Is there any api missing in the above flow for capturing payments.Please help me.
Paypal Express payment method doesn't support online capturing. There is no way to get a full order creation flow like on Checkout via Magento API interface. It is impossible to change the order state and process payments. As a workaround try the following:
Create a custom payment method
Enable for REST API only(Not on website checkout page)
While making payment using rest api use this method (after successful payment using you android/ios SDK)
After placing the order make send transaction id(PAY-xxxxx) return by paypal sdk payment to save trasaction.(tell your server side tio implement this call).
I am writting a complete atrticle regarding this step by step. I will let you know when it is done.
Place Order by PayPal Rest API
For the Place order by the Paypal rest API, you need an active cart with shipping and billing address
By default, we need to follow the few setups for placing the order
• Step 1. Create an empty cart
• Step 2. Add products to the cart
• Step 3. Set the shipping address
• Step 4. Set billing address
• Step 5. Set the delivery method
• Step 6. Apply a coupon (if you have)
• Step 7. Set the payment method
• Step 8. Place order
After steps 6 follow below APIs
We need to call the below APIs one by one for placing the order with Paypal
Create Paypal Express Token
URL : {you website
url}/rest/default/V1/paypalapi/createpaypalexpresstoken
Method: POST
Set Bearer Token in the Hearer (if customer ) for the guest user no need to set it
Content-type : JSON
Body For guest users:
{
"cart_id":"5QWFYZdyccucvgD2QMLDCp5fhjmaH2xg",
"cancel_url":"cancel_url",
"return_url":"return_url"
}
Body For Customer user:
{
"cart_id": 22,
"cancel_url": "cancel_url",
"return_url": "return_url"
}
You will get the response like this :
[
{
"code": 200,
"token": "EC-4MD50688YD296870K",
"paypal_urls":{
"start": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-4MD50688YD296870K&useraction=commit",
"edit": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&useraction=continue&token=EC-4MD50688YD296870K"
}
}]
Explanation
you need to redirect the customer to the {start URL } and after making the payment, PayPal will redirect the user with two params in the redirect URL "payer_id" and "token" Both values you need to call in the below API
Set Payment Method On Cart
URL : {you website
url}/rest/default/V1/paypalapi/setpaymentmethodoncart
Method: POST
Set Bearer Token in the Hearer (if customer ) for the guest user no
need to set it
Content-type : JSON
Body For guest users:
{
"cart_id": "5QWFYZdyccucvgD2QMLDCp5fhjmaH2xg",
"payer_id": "9T3GV67ZSL378",
"token": "EC-4MD50688YD296870K",
"payment_method": "paypal_express"
}
Body For Customer user:
{
"cart_id": 22,
"payer_id": "9T3GV67ZSL378",
"token": "EC-4MD50688YD296870K",
"payment_method": "paypal_express",
"customer_id": 141
}
You will get the response like this :
[
{
"code": 200,
"selected_payment_method": {
"code": "paypal_express",
"title": "PayPal Express Checkout"
}
}
]
Place Order
URL : {you website url}/rest/default/V1/paypalapi/placeorder
Method: POST
Set Bearer Token in the Hearer (if customer ) for the guest user no
need to set it
Content-type : JSON
Body For guest users:
{ "cart_id": "5QWFYZdyccucvgD2QMLDCp5fhjmaH2xg" }
Body For Customer user:
{ "cart_id": 22, "customer_id": 141 }
You will get the response like this :
[ { "code": 200, "order_number": 000000142 } ]
Here is the link to the Paypal Rest API module
https://github.com/santosh-gaggle/paypal-rest-api
In case someone still looking the solution.
In the time I'm answering this, You will need to create a Magento 2 module to process the payment ID.
After you receive the response from in example Paypal android SDK.
Below is the JSON format that you can send to Magento endpoint :
for logged user : PUT /V1/carts/mine/order
for guest : PUT /V1/guest-carts/:cartId/order
Referrence : http://devdocs.magento.com/swagger
The "paypal_express_payment_payload" is just a custom attribute to hold the paypal payment response previously from android SDK.
{
"paymentMethod": {
"method": "paypal_express",
"additional_data": {
"paypal_express_payment_payload": "{\"create_time\":\"2017-06-15T23:13:52Z\",\"id\":\"PAY-2LB41725NU654612TLFBRIUQ\",\"intent\":\"sale\",\"state\":\"approved\"}"
}
}
}
To process the "paypal_express_payment_payload" data, you can implement a Interceptor in your Magento 2 module :
di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Paypal\Model\Express">
<plugin name="mymodule_magento_paypal_model_express_plugin"
type="Mycompanyorpersonal\Mymodule\Plugin\Paypal\Model\Express"
sortOrder="99999"
disabled="false" />
</type>
</config>
Mycompanyorpersonal\Mymodule\Plugin\Paypal\Model\Express.php
You can find the full PHP codes in my following gist : https://gist.github.com/feelinc/de817030e00adc7ff7001de1807c1835
If you use the below to run a post query replace runPostQuery with your curl request. this will pass a token that already has been successful to magento 2.
$payment['paymentMethod'] = ['method' =>'paypal_express',
'additional_data' => array (
'paypal_express_checkout_token' => $request->query->get('token'),
'paypal_express_checkout_redirect_required' => false,
'paypal_express_checkout_payer_id' => $request->query->get('PayerID')
)];
$completedPayment = $this->runPostQuery('carts/mine/payment-information', $headers, json_encode($payment));
You will need to create a plugin to add the last transaction id to the payment see the above comment, but the above payload to payment-information will allow you to get past _placeOrder function in Paypal\Model\Express.php
The paypal_express_checkout_token is the token passed back to the browser from paypal same as PayerId this allows to check the payment, which will return successful and not require a redirect, but is not the payment reference just the action token.
I'm using this endpoint to request for estimated rate
https://wwwcie.ups.com/webservices/Rate
I noticed that the XML response doesn't contain any delivery date. I do see BusinessDaysInTransit though. However, I'm not sure if it's our responsible to do the manual coding of figuring out what the delivery date is. I couldn't find anything in their api documentation.
You have to use the Time In Transit API instead. The Rate API does not provide the estimated delivery date.
I know this is old but to get this in the rate call you need to change the RequestOption to "RateTimeInTransit" add the DeliveryTimeInformation container to the request:
{
"UPSSecurity":{
"UsernameToken":{
"Username":"removed",
"Password":"removed"
},
"ServiceAccessToken":{
"AccessLicenseNumber":"removed"
}
},
"RateRequest":{
"Request":{
"RequestOption":"RateTimeInTransit",
"TransactionReference":{
"CustomerContext":"ORDER123"
}
},
"Shipment":{
"Shipper":{
"Name":"ShipperName",
"ShipperNumber":"removed",
"Address":{
"AddressLine":[
"123 Sesame St"
],
"City":"Nibley",
"StateProvinceCode":"UT",
"PostalCode":"84321",
"CountryCode":"US"
}
},
"ShipTo":{
"Name":null,
"AttentionName":"Ship to Name",
"Address":{
"AddressLine":[
"123 RealAddress ST"
],
"City":"Hyrum",
"StateProvinceCode":"UT",
"PostalCode":"84319",
"CountryCode":"US",
"ResidentialAddressIndicator":"Y"
}
},
"ShipFrom":{
"Name":"Ima Shipper",
"Address":{
"AddressLine":[
"123 Sesame St"
],
"City":"Nibley",
"StateProvinceCode":"UT",
"PostalCode":"84321",
"CountryCode":"US"
}
},
"Service":{
"Code":"03"
},
"Package":{
"PackagingType":{
"Code":"02"
},
"Dimensions":{
"UnitOfMeasurement":{
"Code":"IN",
"Description":"inches"
},
"Length":"5",
"Width":"4",
"Height":"3"
},
"PackageWeight":{
"UnitOfMeasurement":{
"Code":"Lbs",
"Description":"pounds"
},
"Weight":"1"
}
},
"ShipmentRatingOptions":{
"NegotiatedRatesIndicator":"Y",
"RateChartIndicator":"Y"
},
"DeliveryTimeInformation":{
"PackageBillType": "03",
"Pickup":{
"Date":"20191224"
}
}
}
}
If using the UPS JSON Developer Guide you will need to combine info from WebServices guide to know which containers you are missing since the JSON documentation is poor.