Skip to content

Instantly share code, notes, and snippets.

@gauravsoti1
Last active January 21, 2018 19:59
Show Gist options
  • Save gauravsoti1/c2dc7e709401e89be5cf5701c917dd97 to your computer and use it in GitHub Desktop.
Save gauravsoti1/c2dc7e709401e89be5cf5701c917dd97 to your computer and use it in GitHub Desktop.
all about creating and cancelling shopify orders on delhivery
#Making a table shopify_orders to store some data which helps us keep the shopify and delhivery data in sync
class CreateShopifyOrders < ActiveRecord::Migration
def change
create_table :shopify_orders do |t|
t.string :order_id
t.string :order_name
t.string :sub_order_id
t.string :fulfillment_id
t.string :godam_status
t.string :tracking_number
t.timestamps
end
add_index :shopify_orders, :order_id
add_index :shopify_orders, :sub_order_id
end
end
This gist shows the complete flow of creating a shopify order on delhivery
This code is part of a rails project
shopify_controller.rb contains:
Shopify's webhook for order create and order cancellation
Delhivery Webhook for getting the tracking number which we use to update the tracking number in shopify order
sample_shopify_order_response.rb contains:
this is just a dummy file to show you what kind of response shopify gives
delhivery_service.rb contains:
All the helper methods to access the delhivery api easily
also contains function to convert shopify response into response that delhivery's order create api understands
shopify_service.rb contains:
All the helper methods to access the shopify api easily
213412242421_create_shopify_orders.rb
Database migration to save shopify orders and delhivery data to keep both in sync
routes.rb contains:
routes for shopify
# Sample curl request for creating and cancelling orders at delhivery is also provided
#sample delhivery order create curl request, copied from postman
curl -X POST \
https://sandbox-api-godam.delhivery.com/order/create/ \
-H 'authorization: Token <your token here>' \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-H 'postman-token: <your postman token>' \
-H 'version: V2' \
-d '[
{
"order_number": 215650567215,
"order_date": "2018-01-17T17:47:36+05:30",
"consignee": {
"name": "First Second",
"state": "DL",
"city": "New Delhi",
"address2": "test",
"address1": "sdlkflsakdl",
"country": "India",
"pincode": "110016",
"phone1": 9999999999,
"phone2": 0,
"email": null,
"gstin": null
},
"sub_orders": [
{
"sub_order_number": 374233928735,
"access_key": "204c189ac25c82442b500ad59ad55ae4",
"fulfillment_center": "LLDCDE",
"gstin": null,
"expected_ship_date": null,
"dispatch_after_date": null,
"product_details": {
"description": "Dummy Product",
"hsn_code": 2040134963247,
"name": "Dummy Product",
"number": "273636098095",
"quantity": "1",
"sku": "Dummy Product"
},
"shipment_details": {
"courier": "Delhivery",
"packing_slip_link": "",
"payment_mode": "COD",
"shipment_number": "100573085743",
"shipping_level": "",
"sorting_code": "26360",
"waybill_number": ""
},
"invoice_details": {
"advance_payment": 0,
"cgst_amount": 76.27,
"cgst_percentage": 9.0,
"cod_amount": "1050.00",
"cst_percentage": 0,
"discount": 0,
"gross_value": "1000.00",
"igst_amount": 0,
"igst_percentage": 0,
"imei": "null",
"invoice_date": null,
"invoice_link": "",
"invoice_number": "",
"mrp": "1000.00",
"net_amount": "1000.00",
"reference_number": null,
"round_off": 0,
"sgst_amount": 76.27,
"sgst_percentage": 9.0,
"shipping_price": 50,
"tax_percentage": 0,
"total_cst": 0,
"total_price": "1050.00",
"total_taxes": "152.54",
"total_vat": 0,
"unit_price": 0,
"unit_taxes": 0,
"vat_percentage": 0
}
}
]
}
]'
class DelhiveryService
# body is the sample input data that delhivery understands for order creation
def self.create_order(body)
puts "body for creating delhivery order = "
puts body.to_json
api_end_point = DELHIVERY_API_ENDPOINT + "/order/create/"
url = URI(api_end_point)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/json'
request["authorization"] = "Token " + DELHIVERY_TOKEN
request["version"] = 'V2'
request["cache-control"] = 'no-cache'
request.body = "#{body.to_json}"
response = http.request(request)
response_message = response.read_body
response_message = JSON.parse response_message
# puts "request response:::::::::::::::::"
puts response_message.inspect
return response_message["acknowledged"] == true
end
def self.cancel_order(order_id, sub_order_id)
url = URI(DELHIVERY_ENDPOINT + "/oms/api/update/CAN/?client_store=#{DELHIVERY_CLIENT_STORE}&fulfillment_center=#{DELHIVERY_FULFILLMENT_CENTRE}&version=2014.09&order_id=#{order_id}&sub_order_id=#{sub_order_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Put.new(url)
request["content_type"] = 'application/x-www-form-urlencoded'
request["accept"] = 'application/json'
request["authorization"] = "Token " + DELHIVERY_TOKEN
response = http.request(request)
# Fail message: {"status"=>"Fail", "message"=>"No SubOrder against Order to cancel"}
return response
end
def self.create_delhivery_data_from_shopify_data(shr)
delhivery_data = []
delhivery_data << get_order_details(shr)
return delhivery_data
end
def self.create_last_order_from_shopify
orders = ShopifyService.get_orders(Date.yesterday, Date.tomorrow)
order = orders["orders"].first
data = DelhiveryService.create_delhivery_data_from_shopify_data(order)
x = DelhiveryService.create_order data
puts x.to_json #debugging response
return x
end
private
def self.get_order_details(shr)
puts "getting order details"
customer = shr["customer"]
shipping_address = shr["shipping_address"]
products = shr["line_items"]
shipping_array = shr["shipping_lines"]
phone_number = shipping_address["phone"].gsub(/\s/,"").to_i rescue 9999999999
order_object = {
"order_number": shr["name"],
"order_date": shr["created_at"] ,#"2017-07-20T11:14:13.695Z"
"consignee": {
"name": shipping_address["first_name"] + " " + shipping_address["last_name"],
"state": shipping_address["province_code"],
"city": shipping_address["city"],
"address2": "",
"address1": shipping_address["address1"],
"country": shipping_address["country"],
"pincode": shipping_address["zip"],
"phone1": phone_number ,
"phone2": 0,
"email": customer["email"],
"gstin": ""
}
}
sub_orders = []
shr["line_items"].each_with_index do |sub_order,i|
sub_orders << get_sub_order_details((shr["shipping_lines"])[0], sub_order, shipping_address, shr)
end
order_object["sub_orders"] = sub_orders
return order_object
end
#TODO: store access_key in constants
def self.get_sub_order_details(shipping_line, sub_order, shipping_address, shr)
puts "getting sub_order details"
tax_array = sub_order["tax_lines"]
tax_helper_object = ShopifyHelper::Tax.new(tax_array)
shipping_code = shipping_line["code"] rescue ""
{
"sub_order_number": sub_order["id"],
"access_key": DELHIVERY_ACCESS_KEY,
"fulfillment_center": DELHIVERY_FULFILLMENT_CENTRE,
"gstin": "07GGJCPG323A1ZR",
"expected_ship_date": nil,
"dispatch_after_date": nil,
"product_details": {
"description": sub_order["name"],
"hsn_code": "2314",
"name": sub_order["name"],
"number": sub_order["sku"].to_s,
"quantity": sub_order["quantity"].to_s,
"sku": sub_order["sku"]
},
"shipment_details": {
"courier": "Delhivery",
"packing_slip_link": "",
"payment_mode": shr["gateway"] == "cash_on_delivery" ? "COD" : "PREPAID",
"shipment_number": shr["name"],
"shipping_level": "",
"sorting_code": shipping_code,
"waybill_number": ""
},
"invoice_details": {
"advance_payment": shr["gateway"]== "cash_on_delivery" ? 0 : shr["total_price"],
"cgst_amount": tax_helper_object.get_amount("CGST"),
"cgst_percentage": tax_helper_object.get_percentage("CGST") ,
"cod_amount": shr["gateway"]== "cash_on_delivery" ? shr["total_price"] : 0,
"cst_percentage": 0,
"discount": shr["total_discounts"].to_f,
"gross_value": (sub_order["price"].to_f * sub_order["quantity"].to_i).to_s,
"igst_amount": tax_helper_object.get_amount("IGST"),
"igst_percentage": tax_helper_object.get_percentage("IGST"),
"imei": "null",
"invoice_date": shr["created_at"],
"invoice_link": "",
"invoice_number": shr["name"],
"mrp": sub_order["price"].to_s,
"net_amount": ((sub_order["price"].to_f * sub_order["quantity"].to_i) + (shipping_line)["price"].to_i ).to_s,
"reference_number": nil,
"round_off": 0,
"sgst_amount": tax_helper_object.get_amount("SGST"),
"sgst_percentage": tax_helper_object.get_percentage("SGST"),
"shipping_price": (shipping_line)["price"].to_i,
"tax_percentage": 0,
"total_cst": 0,
"total_price": shr["total_price"],
"total_taxes": shr["total_tax"],
"total_vat": 0,
"unit_price": 0,
"unit_taxes": 0,
"vat_percentage": 0
}
}
end
end
# config/environments/development.rb
#this file contains the constants used in the code
# ------------- shopify credentials -----------------
SHOPIFY_ADMIN_USERNAME = "<your shopify private app admin username>"
SHOPIFY_ADMIN_PASSWORD = "<your shopify private app admin password>"
SHOPIFY_SHARED_SECRET = "<your shopify private app shared secret>" # can be used to verify source in the webhook method
SHOPIFY_ENDPOINT = "<your shopify admin api endpoint>" # Example: https://testwebsite-staging.myshopify.com/admin
DELHIVERY_TOKEN = "<your delhivery token here>"
DELHIVERY_ACCESS_KEY = "<your delhivery access key>"
# this end point is used for order creation
DELHIVERY_API_ENDPOINT = "https://sandbox-api-godam.delhivery.com"
# this end point is used for order cancellation
DELHIVERY_ENDPOINT = "https://sandbox-godam.delhivery.com"
DELHIVERY_FULFILLMENT_CENTRE = "<your delhivery fulfillment center>" # will be provided by delhivery
DELHIVERY_CLIENT_STORE = "<your delhivery client store>" # will be provided by delhivery
# ----------------------------------------------------
# ------------- shopify ecommerce ----------------
get "/shopify/api/pincode_eta", to: "shopify#get_eta_from_pincode"
post "/shopify/orders/create", to: "shopify#order_create_response"
post "/shopify/orders/tracking_response", to: "shopify#tracking_response"
post "/shopify/orders/cancellation_response", to: "shopify#cancellation_response"
# ------------------------------------------------
# Sample curl request for order cancelling at delhivery
curl -X PUT \
'https://sandbox-godam.delhivery.com/oms/api/update/CAN/?client_store=%3Cyour_client_store%3E&fulfillment_center=%3Cyour_fulfillment_center%3E&version=2014.09&order_id=TEST1016EPOPXO&sub_order_id=278508305711' \
-H 'accept: application/json' \
-H 'authorization: Token <your_token>' \
-H 'cache-control: no-cache' \
-H 'content_type: application/x-www-form-urlencoded' \
-H 'postman-token: 813dfd35-be76-46b6-1578-347f91ed4042'
# this file contains the sample delhivery response with the tracking number
# this response is the case for only one type sub_order
{
"orderlines": [
{
"status": "PAK",
"courier": "Delhivery",
"weight": "0.0",
"order_id": "TEST1021",
"waybill": "1623110010010", #tracking number
"order_line_id": "326642412079", #sub_order_id
"invoice_date": "2018-01-19T19:41:40.000+05:30",
"fulfillment_center": "DELLL2", # Delhivery provides you this
"client_store": "my delhivery client store", # Delhivery provides you this
"products": [
{
"status": "PAK",
"prod_sku": "1032010100017",
"prod_num": "1032010100017",
"prod_qty": 2,
"imei": "",
"prod_name": "Test Product"
}
]
}
],
"controller": "shopify",
"action": "tracking_response",
"shopify": {
"orderlines": [
{
"status": "PAK",
"courier": "Delhivery",
"weight": "0.0",
"order_id": "TEST1021",
"waybill": "1623110010010",
"order_line_id": "326642412079",
"invoice_date": "2018-01-19T19:41:40.000+05:30",
"fulfillment_center": "DELLL2",
"client_store": "my delhivery client store",
"products": [
{
"status": "PAK",
"prod_sku": "1032010100017",
"prod_num": "1032010100017",
"prod_qty": 2,
"imei": "",
"prod_name": "Test Product"
}
]
}
]
}
}
# this file contains sample shopify response on order creation
# I don't understand why shopify repeats the same data that it gives inside the shopify object as well
{
"id": 14959607852,
"email": "gauravsobti1@gmail.com",
"closed_at": null,
"created_at": "2018-01-12T16:22:21+05:30",
"updated_at": "2018-01-12T16:22:26+05:30",
"number": 18,
"note": null,
"token": "029507bae69esd0f30800c178d305daa",
"gateway": "cash_on_delivery",
"test": false,
"total_price": "1638.00",
"subtotal_price": "1598.00",
"total_weight": 1000,
"total_tax": "243.76",
"taxes_included": true,
"currency": "INR",
"financial_status": "pending",
"confirmed": true,
"total_discounts": "0.00",
"total_line_items_price": "1598.00",
"cart_token": "dcaa2b5adce3f175d89d68sddd31085a",
"buyer_accepts_marketing": true,
"name": "#TEST1018",
"referring_site": "",
"landing_site": "/password",
"cancelled_at": null,
"cancel_reason": null,
"total_price_usd": "25.25",
"checkout_token": "d9fc9075d045aba644e5b476102883e5",
"reference": null,
"user_id": null,
"location_id": null,
"source_identifier": null,
"source_url": null,
"processed_at": "2018-01-12T16:22:21+05:30",
"device_id": null,
"phone": null,
"customer_locale": "en",
"app_id": 580111,
"browser_ip": null,
"landing_site_ref": null,
"order_number": 1018,
"discount_codes": null,
"note_attributes": null,
"payment_gateway_names": [
"cash_on_delivery"
],
"processing_method": "offsite",
"checkout_id": 26242777132,
"source_name": "web",
"fulfillment_status": null,
"tax_lines": [
{
"title": "CGST",
"price": "121.88",
"rate": 0.09
},
{
"title": "SGST",
"price": "121.88",
"rate": 0.09
}
],
"tags": "",
"contact_email": "gauravsobti1@gmail.com",
"order_status_url": "https://myshopifywebsite.com/26619678/orders/0f30800c178d305daa029507bae69e39/authenticate?key=d11a01f61ef45cb04334a3f49124e7c6",
"line_items": [
{
"id": 31644384492,
"variant_id": 244313756972,
"title": "My Shopify Dummy Product",
"quantity": 2,
"price": "799.00",
"sku": "",
"variant_title": "",
"vendor": "My Company Name",
"fulfillment_service": "manual",
"product_id": 45838550316,
"requires_shipping": true,
"taxable": true,
"gift_card": false,
"name": "My Shopify Dummy Product",
"variant_inventory_management": "shopify",
"properties": null,
"product_exists": true,
"fulfillable_quantity": 2,
"grams": 500,
"total_discount": "0.00",
"fulfillment_status": null,
"tax_lines": [
{
"title": "CGST",
"price": "121.88",
"rate": 0.09
},
{
"title": "SGST",
"price": "121.88",
"rate": 0.09
}
],
"origin_location": {
"id": 3812294700,
"country_code": "IN",
"province_code": "DL",
"name": "myshopifywebsite",
"address1": "some address",
"address2": "",
"city": "New Delhi",
"zip": "110026"
},
"destination_location": {
"id": 9329723658,
"country_code": "IN",
"province_code": "DL",
"name": "Gaurav Sobti",
"address1": "baskh ask sdjkhkjsh ",
"address2": "",
"city": "New Delhi",
"zip": "110026"
}
}
],
"shipping_lines": [
{
"id": 11529682988,
"title": "Standard Shipping (Cash on Delivery)",
"price": "40.00",
"code": "23549",
"source": "Cash on Delivery app",
"phone": null,
"requested_fulfillment_service_id": null,
"delivery_category": null,
"carrier_identifier": "fb5772303f6273ceb1a596e2b039d02d",
"discounted_price": "40.00",
"tax_lines": null
}
],
"billing_address": {
"first_name": "Gaurav",
"address1": "baskh ask sdjkhkjsh ",
"phone": "83768 80048",
"city": "New Delhi",
"zip": "110015",
"province": "Delhi",
"country": "India",
"last_name": "Sobti",
"address2": "",
"company": null,
"latitude": 27.1589251,
"longitude": 76.0457614,
"name": "Gaurav Sobti",
"country_code": "IN",
"province_code": "DL"
},
"shipping_address": {
"first_name": "Gaurav",
"address1": "baskh ask sdjkhkjsh ",
"phone": "83768 80048",
"city": "New Delhi",
"zip": "110015",
"province": "Delhi",
"country": "India",
"last_name": "Sobti",
"address2": "",
"company": null,
"latitude": 27.1589251,
"longitude": 76.0457614,
"name": "Gaurav Sobti",
"country_code": "IN",
"province_code": "DL"
},
"fulfillments": null,
"client_details": {
"browser_ip": "50.74.5.106",
"accept_language": "en-US,en;q=0.9,pt;q=0.8",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
"session_hash": "2198d2ef3dfb91062d5852cb5f6dd9c7",
"browser_width": 1440,
"browser_height": 803
},
"refunds": null,
"customer": {
"id": 41743155244,
"email": "gauravsobti1@gmail.com",
"accepts_marketing": true,
"created_at": "2018-01-11T18:25:06+05:30",
"updated_at": "2018-01-12T16:22:21+05:30",
"first_name": "Gaurav",
"last_name": "Sobti",
"orders_count": 3,
"state": "invited",
"total_spent": "0.00",
"last_order_id": 12958607852,
"note": null,
"verified_email": true,
"multipass_identifier": null,
"tax_exempt": false,
"phone": null,
"tags": "",
"last_order_name": "#TEST1018",
"default_address": {
"id": 58670359804,
"customer_id": 30743154244,
"first_name": "Gaurav",
"last_name": "Sobti",
"company": null,
"address1": "baskh ask sdjkhkjsh ",
"address2": "",
"city": "New Delhi",
"province": "Delhi",
"country": "India",
"zip": "110026",
"phone": "99998 80099",
"name": "Gaurav Sobti",
"province_code": "DL",
"country_code": "IN",
"country_name": "India",
"default": true
}
},
"shopify": {
"id": 14959607852,
"email": "gauravsobti1@gmail.com",
"closed_at": null,
"created_at": "2018-01-12T16:22:21+05:30",
"updated_at": "2018-01-12T16:22:26+05:30",
"number": 18,
"note": null,
"token": "029507bae69esd0f30800c178d305daa",
"gateway": "cash_on_delivery",
"test": false,
"total_price": "1638.00",
"subtotal_price": "1598.00",
"total_weight": 1000,
"total_tax": "243.76",
"taxes_included": true,
"currency": "INR",
"financial_status": "pending",
"confirmed": true,
"total_discounts": "0.00",
"total_line_items_price": "1598.00",
"cart_token": "dcaa2b5adce3f175d89d68sddd31085a",
"buyer_accepts_marketing": true,
"name": "#TEST1018",
"referring_site": "",
"landing_site": "/password",
"cancelled_at": null,
"cancel_reason": null,
"total_price_usd": "25.25",
"checkout_token": "d9fc9075d045aba644e5b476102883e5",
"reference": null,
"user_id": null,
"location_id": null,
"source_identifier": null,
"source_url": null,
"processed_at": "2018-01-12T16:22:21+05:30",
"device_id": null,
"phone": null,
"customer_locale": "en",
"app_id": 580111,
"browser_ip": null,
"landing_site_ref": null,
"order_number": 1018,
"discount_codes": null,
"note_attributes": null,
"payment_gateway_names": [
"cash_on_delivery"
],
"processing_method": "offsite",
"checkout_id": 26242575132,
"source_name": "web",
"fulfillment_status": null,
"tax_lines": [
{
"title": "CGST",
"price": "121.88",
"rate": 0.09
},
{
"title": "SGST",
"price": "121.88",
"rate": 0.09
}
],
"tags": "",
"contact_email": "gauravsobti1@gmail.com",
"order_status_url": "https://myshopifywebsite.com/26619678/orders/0f30800c178d305daa029507bae69e39/authenticate?key=d11a01f61ef45cb04334a3f49124e7c6",
"line_items": [
{
"id": 31644384492,
"variant_id": 244313756972,
"title": "My Shopify Dummy Product",
"quantity": 2,
"price": "799.00",
"sku": "",
"variant_title": "",
"vendor": "My Company Name",
"fulfillment_service": "manual",
"product_id": 45838550316,
"requires_shipping": true,
"taxable": true,
"gift_card": false,
"name": "My Shopify Dummy Product",
"variant_inventory_management": "shopify",
"properties": null,
"product_exists": true,
"fulfillable_quantity": 2,
"grams": 500,
"total_discount": "0.00",
"fulfillment_status": null,
"tax_lines": [
{
"title": "CGST",
"price": "121.88",
"rate": 0.09
},
{
"title": "SGST",
"price": "121.88",
"rate": 0.09
}
],
"origin_location": {
"id": 3812294700,
"country_code": "IN",
"province_code": "DL",
"name": "myshopifywebsite",
"address1": "some address",
"address2": "",
"city": "New Delhi",
"zip": "110026"
},
"destination_location": {
"id": 9329723658,
"country_code": "IN",
"province_code": "DL",
"name": "Gaurav Sobti",
"address1": "baskh ask sdjkhkjsh ",
"address2": "",
"city": "New Delhi",
"zip": "110026"
}
}
],
"shipping_lines": [
{
"id": 11529682988,
"title": "Standard Shipping (Cash on Delivery)",
"price": "40.00",
"code": "23549",
"source": "Cash on Delivery app",
"phone": null,
"requested_fulfillment_service_id": null,
"delivery_category": null,
"carrier_identifier": "fb5772303f6273ceb1a596e2b039d02d",
"discounted_price": "40.00",
"tax_lines": null
}
],
"billing_address": {
"first_name": "Gaurav",
"address1": "baskh ask sdjkhkjsh ",
"phone": "83768 80048",
"city": "New Delhi",
"zip": "110015",
"province": "Delhi",
"country": "India",
"last_name": "Sobti",
"address2": "",
"company": null,
"latitude": 27.1589251,
"longitude": 76.0457614,
"name": "Gaurav Sobti",
"country_code": "IN",
"province_code": "DL"
},
"shipping_address": {
"first_name": "Gaurav",
"address1": "baskh ask sdjkhkjsh ",
"phone": "83768 80048",
"city": "New Delhi",
"zip": "110015",
"province": "Delhi",
"country": "India",
"last_name": "Sobti",
"address2": "",
"company": null,
"latitude": 27.1589251,
"longitude": 76.0457614,
"name": "Gaurav Sobti",
"country_code": "IN",
"province_code": "DL"
},
"fulfillments": null,
"client_details": {
"browser_ip": "50.74.5.106",
"accept_language": "en-US,en;q=0.9,pt;q=0.8",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
"session_hash": "2198d2ef3dfb91062d5852cb5f6dd9c7",
"browser_width": 1440,
"browser_height": 803
},
"refunds": null,
"customer": {
"id": 41743155244,
"email": "gauravsobti1@gmail.com",
"accepts_marketing": true,
"created_at": "2018-01-11T18:25:06+05:30",
"updated_at": "2018-01-12T16:22:21+05:30",
"first_name": "Gaurav",
"last_name": "Sobti",
"orders_count": 3,
"state": "invited",
"total_spent": "0.00",
"last_order_id": 12958607852,
"note": null,
"verified_email": true,
"multipass_identifier": null,
"tax_exempt": false,
"phone": null,
"tags": "",
"last_order_name": "#TEST1018",
"default_address": {
"id": 58670359804,
"customer_id": 30743154244,
"first_name": "Gaurav",
"last_name": "Sobti",
"company": null,
"address1": "baskh ask sdjkhkjsh ",
"address2": "",
"city": "New Delhi",
"province": "Delhi",
"country": "India",
"zip": "110026",
"phone": "99998 80099",
"name": "Gaurav Sobti",
"province_code": "DL",
"country_code": "IN",
"country_name": "India",
"default": true
}
}
}
}
class ShopifyController < ApplicationController
skip_before_filter :verify_authenticity_token, only:[:order_create_response,:tracking_response,:cancellation_response]
# shopify webhook for order create
# We store order_id, order_name, and sub_order_ids in our database to maintain data authenticity
# This function parses shopify response and creates order at delhivery
# We store all the sub_order_id separately to handle the multiple product type case
# because multiple sub_orders can have different types of shipping fulfillments, one might reach early and one late
def order_create_response
puts params.to_json
order_id = params["id"].to_s
sub_orders = params["line_items"]
sub_orders.each do |so|
shopify_order = ShopifyOrder.find_or_initialize_by( order_id: order_id, sub_order_id: so["id"].to_s )
shopify_order.order_name = params["name"]
shopify_order.save
end
data = DelhiveryService.create_delhivery_data_from_shopify_data(params)
is_success = DelhiveryService.create_order(data)
if is_success
ShopifyOrder.where(order_id: order_id).update_all(godam_status: "submitted")
else
Bugsnag.notify("error creating an order on delhivery for shopify order no #{order_id}")
end
render json: {message: "Success"}, status: 200 and return
end
#called by shopify when an order is cancelled
#we then call the delhivery api and cancel the order there as well
#in our shopify website frontend, we handle the scenario where cancellation is not allowed if the order has already been shipped
def cancellation_response
sub_orders = params["line_items"]
sub_order_id = sub_orders[0]["id"].to_s
order_id = params["id"].to_s
order_name = params["name"]
shopify_order = ShopifyOrder.find_by(order_id: order_id, sub_order_id: sub_order_id)
# if order not cancelled
if shopify_order.present?
if shopify_order.godam_status != "cancelled"
response = DelhiveryService.cancel_order(order_name, sub_order_id)
response_body = JSON.parse(response.read_body)
if response.code == "200"
shopify_order.update(godam_status: "cancelled")
else
Bugsnag.notify("Couldn't cancel Shopify order: #{order_name} on delhivery because: " + response_body["message"])
end
else
puts "Order #{order_name} already cancelled"
end
else
Bugsnag.notify("Shopify order: #{order_name} not present")
end
render json: {message: "success"}, status: 200 and return
end
# This function is called by delhivery service with the tracking number, which we use to update the shopify order
# If the tracking number is present in response, we create shopify fulfillment agianst the order
# Which in turn notifies the user regarding shipping status and gives the user tracking number to track the order
def tracking_response
puts params.to_json
orderlines = params
orderlines["orderlines"].each do |sub_order|
order_name = sub_order["order_id"].to_s
tracking_number = sub_order["waybill"]
sub_order_id = sub_order["order_line_id"].to_s
godam_status = sub_order["status"]
if tracking_number.present?
shopify_order = ShopifyOrder.find_by(order_name: order_name, sub_order_id: sub_order_id)
if shopify_order.present?
shopify_order.tracking_number = tracking_number
shopify_order.godam_status = godam_status
shopify_order.save
if !shopify_order.fulfillment_id.present?
fulfillment_id = update_tracking_number(shopify_order.order_id, sub_order_id, tracking_number)
if fulfillment_id.present?
shopify_order.fulfillment_id = fulfillment_id
shopify_order.save
end
else
puts "fulfillment for order_id = #{shopify_order.order_id} also exists and its id = #{shopify_order.fulfillment_id}"
end
else
Bugsnag.notify("Couldn't find shopify order with order name = #{order_name} and sub_order_id = #{sub_order_id}")
end
end
end
render json: {message: "Successfully received response"}, status: 200 and return
end
private
def update_tracking_number(order_id, sub_order_id, tracking_number)
fulfillment_id = ShopifyService.update_tracking_number(order_id, tracking_number, "Delhivery", sub_order_id )
return fulfillment_id
end
end
module ShopifyHelper
class Tax
# "tax_lines" array inside the "line_items" array is passed here
# then it becomes easy to get value or a default value of a certain type of tax
def initialize(tax_array)
@tax_array = tax_array
end
def get_tax_type(tax_type)
@tax_array.find{|x| x["title"] == tax_type}
end
def get_percentage(tax_type)
(get_tax_type(tax_type)['rate'] * 100) rescue 0
end
def get_amount(tax_type)
(get_tax_type(tax_type)['price'].to_f) rescue 0.0
end
end
end
class ShopifyService
def self.update_tracking_number(order_number, tracking_number, tracking_company, sub_order_id)
api_end_point = SHOPIFY_ENDPOINT + "/orders/#{order_number}/fulfillments.json"
fulfillment_object = {}
fulfillment_object["tracking_number"] = tracking_number
fulfillment_object["tracking_company"] = tracking_company
fulfillment_object["line_items"] = []
line_item = {}
line_item["id"] = sub_order_id.to_i
fulfillment_object["line_items"] << line_item
fulfillment_body = {"fulfillment": fulfillment_object }
response = HTTParty.post(api_end_point, :body => fulfillment_body.to_json, :headers => get_headers, :basic_auth => get_auth )
if response["errors"].present?
Bugsnag.notify(response["errors"].to_s)
return nil
else
return response["fulfillment"]["id"]
end
end
def self.create_webhook(url,topic)
# topic = "orders\/create", "orders\/cancelled"
api_end_point = SHOPIFY_ENDPOINT + "/webhooks.json"
body = {
"webhook": {
"topic": topic,
"address": url,
"format": "json"
}
}
response = HTTParty.post(api_end_point, :body => body.to_json, :headers => get_headers, :basic_auth => get_auth )
end
def self.get_orders(from_date, to_date, page = nil, status=nil)
# statuses:
# open - All open orders (default)
# closed - Show only closed orders
# cancelled - Show only cancelled orders
# any - Any order status
api_end_point = SHOPIFY_ENDPOINT + "/orders.json"
params = {}
params["created_at_min"] = from_date.to_formatted_s(:iso8601)
params["created_at_max"] = to_date.to_formatted_s(:iso8601)
params["status"] = status.present? ? status : "open"
params["page"] = page.present? ? page : 1
response = HTTParty.get(api_end_point, query: params, :headers => get_headers, :basic_auth => get_auth )
return response
end
def self.get_order(order_id)
api_end_point = SHOPIFY_ENDPOINT + "/orders/" + order_id.to_s + ".json"
params = {}
response = HTTParty.get(api_end_point, query: params, :headers => get_headers, :basic_auth => get_auth )
return response
end
def self.webhooks
api_end_point = SHOPIFY_ENDPOINT + "/webhooks.json"
response = HTTParty.get(api_end_point, :body => {}, :headers => get_headers, :basic_auth => get_auth )
return response
end
def self.get_fulfillments(order_id)
api_end_point = SHOPIFY_ENDPOINT + "/orders/#{order_id}/fulfillments.json"
response = HTTParty.get(api_end_point, :body => {}, :headers => get_headers, :basic_auth => get_auth )
return response
end
private
def self.get_headers
headers = {}
headers["Content-Type"] = "application/json"
return headers
end
def self.get_auth
{username: SHOPIFY_ADMIN_USERNAME, password: SHOPIFY_ADMIN_PASSWORD}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment