Discussing APIs and development related to customers, discounts, and order management.
Please note: This is a two-part question:
PART I
According to the Order API documentation payment_details has been deprecated and we're told to use "Transaction resource" instead.
So, I added "transactions" to my "order" like so...
{
"order": {
"transaction": {
"currency": "USD",
"amount": 219.8,
"kind": "sale",
"authorization": "",
"status": "success",
"gateway": "Authorize.net"
},
I am able to POST the order without error, however, I cannot retrieve any transactions, nor do I see a transaction comment on the order itself.
I have tried omitting financial_status, changing the various values passed within the transaction, and numerous other changes--all to no avail. Also, no matter what I do, the order shows as "Paid.
PART 2
Our client would like to have both approved and declined transactions saved to their storefront. Assuming I get the transaction working, is posting a declined order as simple as setting transaction.status = "failure"?
Can you please help?
Solved! Go to the solution
This is an accepted solution.
Hi Daniel,
This topic keeps popping up on the forums and there seems to be a lot of confusion around creating orders via API, but not being able to collect payment information or transactions.
Let's begin with documentation straight from the Order API
Orders can be created through the API, but no payment information will be collected, and no transaction performed. You can mark the order with any payment status.
You can also allow merchants to create orders manually by using the DraftOrder resource.
So there we have it. Which makes sense. If you create an order via the Shopify Admin UI, notice you really are creating a draft order first. Which is what you need to do with the API as well. Looking at the Draft Order API
You can use the DraftOrder resource to allow merchants to create orders on behalf of customers. This is useful for Shopify merchants who receive orders through outside channels
Your app IS an outside channel. Anyway, let us create a draft order
1. Create Draft Order
POST /admin/draft_orders.json
{
"draft_order": {
"line_items": [
{
"variant_id": <SOME_PRODUCT_VARIANT_ID>,
"quantity": 1
}
]
}
}
The response will provide you with the draft order id for the next request
2. Complete Draft Order
PUT /admin/draft_orders/<DRAFT_ORDER_ID>/complete.json?payment_pending=true
The response will provide you with the order_id for the next step
3. Mark Order as paid
Now what's important to understand is that with completing your draft order and transfering it to an order, you have also implicitly created a transaction. Since we didn't specify otherwise on our draft order, the default (in my dev store) is a manual transaction. Let's check it, we will need the transaction ID anyway.
GET /admin/orders/<ORDER_ID>/transactions.json
// Query Response
{
"transactions": [
{
"id": <TRANSACTION_ID>,
"order_id": <ORDER_ID>,
"kind": "sale",
"gateway": "manual",
"status": "pending",
"message": "Pending the manual payment from the buyer",
"created_at": "2019-01-02T11:57:43-12:00",
"test": false,
"authorization": null,
"location_id": null,
"user_id": null,
"parent_id": null,
"processed_at": "2019-01-02T11:57:43-12:00",
"device_id": null,
"receipt": {},
"error_code": null,
"source_name": "2632381",
"amount": "6.00",
"currency": "CZK",
"admin_graphql_api_id": "gid:\/\/shopify\/OrderTransaction\/1041253236851"
}
]
}
See gateway is manual since that's my default and status is pending as when I completed the draft order in step 2, I passed along the querystring argument payment_pending=true. To mark it as paid, I need to create a new transaction referencing the parent transaction I am referring to.
POST /admin/orders/<ORDER_ID>/transactions.json
{
"transaction": {
"parent_id": <TRANSACTION_ID>,
"currency": "CZK",
"amount": "6.00",
"kind": "capture"
}
}
Hang on, why capture? Well the entire authorization, capture etc. lingo is just a common denominator from card processing, but applies just as well to any other kind. When we created the draft order, we basically said the customer authorized us to take their 6.00 CZK and when completing that draft and marking its payment as pending, we just noted that we are waiting to receive that issued amount - which once we do, we capture - thus kind is capture even for manual payments.
And that's that. I'll leave other payment methods for you to explore , but generally advise to reading the documentation TOP to bottom and not jumping straight to examples and making assumptions though I can't blame you, we all do sometime 😉
Hope this helps!
Attempting to submit the transaction on its own via admin/orders/#order/transactions.json results in:
{
"errors": {
"kind": [
"sale is not a valid transaction"
],
"currency": []
}
}
This is an accepted solution.
Hi Daniel,
This topic keeps popping up on the forums and there seems to be a lot of confusion around creating orders via API, but not being able to collect payment information or transactions.
Let's begin with documentation straight from the Order API
Orders can be created through the API, but no payment information will be collected, and no transaction performed. You can mark the order with any payment status.
You can also allow merchants to create orders manually by using the DraftOrder resource.
So there we have it. Which makes sense. If you create an order via the Shopify Admin UI, notice you really are creating a draft order first. Which is what you need to do with the API as well. Looking at the Draft Order API
You can use the DraftOrder resource to allow merchants to create orders on behalf of customers. This is useful for Shopify merchants who receive orders through outside channels
Your app IS an outside channel. Anyway, let us create a draft order
1. Create Draft Order
POST /admin/draft_orders.json
{
"draft_order": {
"line_items": [
{
"variant_id": <SOME_PRODUCT_VARIANT_ID>,
"quantity": 1
}
]
}
}
The response will provide you with the draft order id for the next request
2. Complete Draft Order
PUT /admin/draft_orders/<DRAFT_ORDER_ID>/complete.json?payment_pending=true
The response will provide you with the order_id for the next step
3. Mark Order as paid
Now what's important to understand is that with completing your draft order and transfering it to an order, you have also implicitly created a transaction. Since we didn't specify otherwise on our draft order, the default (in my dev store) is a manual transaction. Let's check it, we will need the transaction ID anyway.
GET /admin/orders/<ORDER_ID>/transactions.json
// Query Response
{
"transactions": [
{
"id": <TRANSACTION_ID>,
"order_id": <ORDER_ID>,
"kind": "sale",
"gateway": "manual",
"status": "pending",
"message": "Pending the manual payment from the buyer",
"created_at": "2019-01-02T11:57:43-12:00",
"test": false,
"authorization": null,
"location_id": null,
"user_id": null,
"parent_id": null,
"processed_at": "2019-01-02T11:57:43-12:00",
"device_id": null,
"receipt": {},
"error_code": null,
"source_name": "2632381",
"amount": "6.00",
"currency": "CZK",
"admin_graphql_api_id": "gid:\/\/shopify\/OrderTransaction\/1041253236851"
}
]
}
See gateway is manual since that's my default and status is pending as when I completed the draft order in step 2, I passed along the querystring argument payment_pending=true. To mark it as paid, I need to create a new transaction referencing the parent transaction I am referring to.
POST /admin/orders/<ORDER_ID>/transactions.json
{
"transaction": {
"parent_id": <TRANSACTION_ID>,
"currency": "CZK",
"amount": "6.00",
"kind": "capture"
}
}
Hang on, why capture? Well the entire authorization, capture etc. lingo is just a common denominator from card processing, but applies just as well to any other kind. When we created the draft order, we basically said the customer authorized us to take their 6.00 CZK and when completing that draft and marking its payment as pending, we just noted that we are waiting to receive that issued amount - which once we do, we capture - thus kind is capture even for manual payments.
And that's that. I'll leave other payment methods for you to explore , but generally advise to reading the documentation TOP to bottom and not jumping straight to examples and making assumptions though I can't blame you, we all do sometime 😉
Hope this helps!
Perfect and thank you, Karl. I'll give this a go.
Your response here should be part of the documentation. I had read the term "draft order" in the documents. However, I was fixated in adding an Order--not a Draft Order (I don't speak Shopify, yet.) The Draft Order needs to be to be clarified in Shopify's documentation, at the very least much easier to pick out--especially given that you mention the topic "keeps popping up on the forums."
Don't take my word for it 😉 I may be wrong or there may be better ways to achieve the same - I am pretty new to Shopify myself. A lot of reading and a lot of reverse *cough* engineering or to be more accurate, network activity monitoring, of what the admin UI does (though A LOT of those APIs are not accessible outside of the admin UI... at least not officially) have helped me understand a few things and might help you too.
i have same error apper
$transactions=array(
"transaction"=>array(
"kind"=> "sale",
"amount" => "585.60",
"currency"=> "AUD",
"status"=>"success",
"gateway"=>"manual"
)
);
POST /admin/orders/#{order_id}/transaction.json
Array ( [errors] => Array ( [kind] => Array ( [0] => sale is not a valid transaction ) [currency] => Array ( ) ) )
Any one can solve this issue
Hi @Ravipatel the answer as to why you're getting that error is in this thread 🙄
create order using api
POST /admin/orders.json/
Then POST /admin/orders/#order_id/transactions.json
$transactions=array( "transaction"=>array( "kind"=> "sale", "amount" => "585.60", "currency"=> "AUD", "status"=>"success", "gateway"=>"manual" ) );
$curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_VERBOSE, 0); curl_setopt($curl, CURLOPT_HEADER, 0); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($transactions)); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec ($curl); curl_close ($curl); $resp = json_decode($response,true);
but still given error
Array ( [errors] => Array ( [kind] => Array ( [0] => sale is not a valid transaction ) [currency] => Array ( ) ) )
Sorry @Ravipatel but