The third use case in the Shopify business scenario is used for shipping. This page describes the related tasks and the operations you use in the Shopify connector and the other ESB connectors
Overview
The flow for shipping is illustrated in the following diagram. The ESB connectors for Shippo will be used to connect to each service.
Assumption
The stock levels in Shopify are automatically updated when the order is processed after payments.
- Retrieve unshipped orders from Shopify using the listOrders operation.
- Create fulfilments for the retrieved orders in the Shopify API using the createOrder operation, only if the shipping address is provided.
The recipient address will be created in the Shippo API using the createAddress operation, and this address will be mapped with the shipping address of the order in the Shopify API before the shipment is created. The shipping address is provided in the order in the Shopify API. Create shipments in the Shippo API using the createShipment operation.
Note
- As a prerequisite, parcels and the sender’s address will be created in the Shippo API as an offline process of ESB.
- Only the “QUOTE” object_purpose and the “DROPOFF” submission_type of the shipment are handled in the Shippo API for this particular scenario.
Shopify operations
Shippo operations
Samples
<?xml version="1.0" encoding="UTF-8"?> <!-- Following template first creates the receiver address in Shippo using the Shopify order's shipping address details. Then it fulfills the order and creates a Shipment in Shippo. --> <template name="shopify-createShipmentsInShippo" xmlns="http://ws.apache.org/ns/synapse"> <!-- Shopify parameters --> <parameter name="shopifyApiUrl" description="The apiUrl of Shopify" /> <parameter name="shopifyAccessToken" description="Encrypted alphanumeric string to authenticate the Shopify credentials" /> <parameter name="shopifyOrderId" description="Shopify Order Identifier to be fulfilled" /> <parameter name="shopifyAddressOne" description="Shipping address one of the order" /> <parameter name="shopifyAddressTwo" description="Shipping address two of the order" /> <parameter name="shopifyCity" description="City of the Shipping address" /> <parameter name="shopifyCompany" description="Company of the Shipping address" /> <parameter name="shopifyPhone" description="Phone number of the Shipping address" /> <parameter name="shopifyProvince" description="Province of the Shipping address" /> <parameter name="shopifyZip" description="Zip code of the Shipping address" /> <parameter name="shopifyName" description="Name on the Shipping address" /> <parameter name="shopifyCountry" description="Country of the Shipping address" /> <!-- Shippo parameters --> <parameter name="shippoApiUrl" description="Shippo Api Url" /> <parameter name="shippoUsername" description="Shippo Username" /> <parameter name="shippoPassword" description="Shippo password" /> <parameter name="shippoAddressFromId" description="Shippo, sender address identifier" /> <parameter name="shippoParcelId" description="Shippo parcel Identifier of the " /> <sequence> <!-- Shopify properties --> <property name="uri.var.shopifyApiUrl" expression="$func:shopifyApiUrl" /> <property name="uri.var.shopifyAccessToken" expression="$func:shopifyAccessToken" /> <property name="uri.var.shopifyOrderId" expression="$func:shopifyOrderId" /> <property name="uri.var.shopifyAddressOne" expression="$func:shopifyAddressOne" /> <property name="uri.var.shopifyAddressTwo" expression="$func:shopifyAddressTwo" /> <property name="uri.var.shopifyCity" expression="$func:shopifyCity" /> <property name="uri.var.shopifyCompany" expression="$func:shopifyCompany" /> <property name="uri.var.shopifyPhone" expression="$func:shopifyPhone" /> <property name="uri.var.shopifyProvince" expression="$func:shopifyProvince" /> <property name="uri.var.shopifyZip" expression="$func:shopifyZip" /> <property name="uri.var.shopifyName" expression="$func:shopifyName" /> <property name="uri.var.shopifyCountry" expression="$func:shopifyCountry" /> <property name="uri.var.shopifyFormat" value="json" /> <!-- Shippo properties --> <property name="uri.var.shippoApiUrl" expression="$func:shippoApiUrl" /> <property name="uri.var.shippoUsername" expression="$func:shippoUsername" /> <property name="uri.var.shippoPassword" expression="$func:shippoPassword" /> <property name="uri.var.shippoAddressFromId" expression="$func:shippoAddressFromId" /> <property name="uri.var.shippoParcelId" expression="$func:shippoParcelId" /> <property name="uri.var.shippoObjectPurpose" value="QUOTE" /> <property name="uri.var.shippoSubmissionType" value="DROPOFF" /> <!-- Remove the Shopify response header information. --> <header name="Connection" scope="transport" action="remove" /> <header name="Content-Encoding" scope="transport" action="remove" /> <header name="Content-Type" scope="transport" action="remove" /> <header name="Date" scope="transport" action="remove" /> <header name="P3P" scope="transport" action="remove" /> <header name="Server" scope="transport" action="remove" /> <header name="Status" scope="transport" action="remove" /> <header name="Transfer-Encoding" scope="transport" action="remove" /> <header name="Vary" scope="transport" action="remove" /> <header name="X-Content-Type-Options" scope="transport" action="remove" /> <header name="X-Frame-Option" scope="transport" action="remove" /> <header name="X-Request-Id" scope="transport" action="remove" /> <header name="X-Served-With" scope="transport" action="remove" /> <header name="X-ShardId" scope="transport" action="remove" /> <header name="X-ShopId" scope="transport" action="remove" /> <header name="X-Stats-UserId" scope="transport" action="remove" /> <header name="X-UA-Compatible" scope="transport" action="remove" /> <header name="X-XSS-Protection" scope="transport" action="remove" /> <property name="messageType" value="application/json" scope="axis2" /> <shippo.init> <apiUrl>{$ctx:uri.var.shippoApiUrl}</apiUrl> <username>{$ctx:uri.var.shippoUsername}</username> <password>{$ctx:uri.var.shippoPassword}</password> </shippo.init> <shippo.createAddress> <objectPurpose>{$ctx:uri.var.shippoObjectPurpose}</objectPurpose> <streetOne>{$ctx:uri.var.shopifyAddressOne}</streetOne> <streetTwo>{$ctx:uri.var.shopifyAddressTwo}</streetTwo> <city>{$ctx:uri.var.shopifyCity}</city> <company>{$ctx:uri.var.shopifyCompany}</company> <phone>{$ctx:uri.var.shopifyPhone}</phone> <state>{$ctx:uri.var.shopifyProvince}</state> <zipCode>{$ctx:uri.var.shopifyZip}</zipCode> <name>{$ctx:uri.var.shopifyName}</name> <country>{$ctx:uri.var.shopifyCountry}</country> <streetNo>{$ctx:streetNo}</streetNo> <email>{$ctx:email}</email> <metadata>{$ctx:metadata}</metadata> </shippo.createAddress> <property name="uri.var.shippoAddressToId" expression="json-eval($.object_id)" /> <!-- Check the successful creation of the receive address to proceed with createShipment. --> <filter source="boolean(get-property('uri.var.shippoAddressToId'))" regex="true"> <else> <!-- Error in creating the receive address. --> <property name="uri.var.shippoCreteAddressErrorMessage" expression="json-eval($)" /> <script language="js"> <![CDATA[ //Replaces the double quotes to maintain the valid JSON response. var shippoCreteAddressErrorMessage = mc.getProperty('uri.var.shippoCreteAddressErrorMessage'); mc.setProperty('uri.var.shippoCreteAddressErrorMessage', shippoCreteAddressErrorMessage.replace('"', '\\"')); ]]> </script> <property name="uri.var.activity" value="shippo_createAddress" /> <property name="uri.var.id" value="" /> <property name="uri.var.status" value="Error" /> <property name="uri.var.message" expression="fn:concat('Could not create the receive address of the shipment, Error: ', get-property('uri.var.shippoCreteAddressErrorMessage'))" /> </else> <then> <!-- Successfully created the receive address. --> <!-- Create the Shippo address ID as the tracking number. --> <property name="uri.var.shopifyFulfillment" expression="fn:concat('{"tracking_number": "', get-property('uri.var.shippoAddressToId'), '", "notify_customer": "', 'true', '"}')" /> <!-- Create fulfillment in Shopify. --> <shopify.init> <accessToken>{$ctx:uri.var.shopifyAccessToken}</accessToken> <apiUrl>{$ctx:uri.var.shopifyApiUrl}</apiUrl> <format>{$ctx:uri.var.shopifyFormat}</format> </shopify.init> <shopify.createFulfillment> <fulfillment>{$ctx:uri.var.shopifyFulfillment}</fulfillment> <orderId>{$ctx:uri.var.shopifyOrderId}</orderId> </shopify.createFulfillment> <property name="uri.var.shopifyFulfillmentId" expression="json-eval($.fulfillment.id)" /> <!-- Check the availability of the fulfillment identifier to proceed. --> <filter source="boolean(get-property('uri.var.shopifyFulfillmentId'))" regex="true"> <else> <!-- Error in fulfilling the order. --> <property name="uri.var.activity" value="shopify_createFulfillment" /> <property name="uri.var.status" value="Error" /> <property name="uri.var.shopifyFulfillmentError" expression="json-eval($..errors[0])" /> <property name="uri.var.message" expression="fn:concat('Could not fulfill the Shopify Order with Order ID:', get-property('uri.var.shopifyOrderId'), get-property('uri.var.shopifyFulfillmentError'))" /> </else> <then> <!-- Successfully fulfilled the order. --> <property name="uri.var.id" expression="get-property('uri.var.shopifyFulfillmentId')" /> <property name="uri.var.message" expression="fn:concat('Successfully fulfilled the Shopify order with Order ID:', get-property('uri.var.shopifyOrderId'))" /> <!-- Response generator for Shopify createFulfillment --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="shopify_createFulfillment" /> <with-param name="id" value="{$ctx:uri.var.id}" /> <with-param name="status" value="Success" /> <with-param name="message" value="{$ctx:uri.var.message}" /> </call-template> <!-- Create Shipment in Shippo. --> <shippo.init> <apiUrl>{$ctx:uri.var.shippoApiUrl}</apiUrl> <username>{$ctx:uri.var.shippoUsername}</username> <password>{$ctx:uri.var.shippoPassword}</password> </shippo.init> <shippo.createShipment> <objectPurpose>{$ctx:uri.var.shippoObjectPurpose}</objectPurpose> <addressFrom>{$ctx:uri.var.shippoAddressFromId}</addressFrom> <addressTo>{$ctx:uri.var.shippoAddressToId}</addressTo> <parcel>{$ctx:uri.var.shippoParcelId}</parcel> <submissionType>{$ctx:uri.var.shippoSubmissionType}</submissionType> <referenceOne>{$ctx:uri.var.shopifyOrderId}</referenceOne> <extra>{$ctx:uri.var.shippoExtra}</extra> <customsDeclaration>{$ctx:uri.var.shippoCustomsDeclaration}</customsDeclaration> <submissionDate>{$ctx:uri.var.shippoSubmissionDate}</submissionDate> <insuranceAmount>{$ctx:uri.var.shippoInsuranceAmount}</insuranceAmount> <addressReturn>{$ctx:uri.var.shippoAddressReturn}</addressReturn> <insuranceCurrency>{$ctx:uri.var.shippoInsuranceCurrency}</insuranceCurrency> <metadata>{$ctx:uri.var.shippoMetadata}</metadata> <referenceTwo>{$ctx:uri.var.shippoReferenceTwo}</referenceTwo> </shippo.createShipment> <property name="uri.var.shippoShipmentId" expression="json-eval($.object_id)" /> <filter source="boolean(get-property('uri.var.shippoShipmentId'))" regex="true"> <else> <!-- Error in creating the Shipment in Shippo. --> <property name="uri.var.shippoCreteShipmentErrorMessage" expression="json-eval($)" /> <script language="js"> <![CDATA[ //Replaces the double quotes to maintain the valid JSON response. var shippoCreteShipmentErrorMessage = mc.getProperty('uri.var.shippoCreteShipmentErrorMessage'); mc.setProperty('uri.var.shippoCreteShipmentErrorMessage', shippoCreteShipmentErrorMessage.replace('"', '\\"')); ]]> </script> <property name="uri.var.activity" value="shippo_createShipment" /> <property name="uri.var.id" value="null" /> <property name="uri.var.status" value="Error" /> <property name="uri.var.message" expression="fn:concat('Could not create the shipment of the shipment, Error: ', get-property('uri.var.shippoCreteShipmentErrorMessage'))" /> </else> <then> <!-- Successfully created the Shipment in Shippo. --> <property name="uri.var.activity" value="shippo_createShipment" /> <property name="uri.var.id" expression="get-property('uri.var.shippoShipmentId')" /> <property name="uri.var.status" value="Success" /> <property name="uri.var.message" value="Successfully created the Shipment in Shippo" /> </then> </filter> <!-- END: createShipment in Shippo. --> </then> <!-- END: Check the successful creation of Shopify fulfillment. --> </filter> <!-- END: Check Shopify createFulfillment. --> </then> <!-- END: Successful creation of receiveAddress. --> </filter> <!-- END: Check the successful creation of Shippo address. --> <!-- Response generator --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="{$ctx:uri.var.activity}" /> <with-param name="id" value="{$ctx:uri.var.id}" /> <with-param name="status" value="{$ctx:uri.var.status}" /> <with-param name="message" value="{$ctx:uri.var.message}" /> </call-template> </sequence> </template>
<?xml version="1.0" encoding="UTF-8"?> <proxy xmlns="http://ws.apache.org/ns/synapse" name="shopify_createShipments" transports="https" statistics="disable" trace="disable" startOnLoad="true"> <target> <inSequence onError="faultHandlerSeq"> <!-- Shopify properties --> <property name="shopifyApiUrl" expression="json-eval($.shopifyApiUrl)" /> <property name="shopifyAccessToken" expression="json-eval($.shopifyAccessToken)" /> <property name="shopifyFormat" value="xml" /> <property name="shopifyFulfillmentStatus" value="unshipped" /> <!-- Shippo Properties --> <property name="shippoApiUrl" expression="json-eval($.shippoApiUrl)" /> <property name="shippoUsername" expression="json-eval($.shippoUsername)" /> <property name="shippoPassword" expression="json-eval($.shippoPassword)" /> <property name="shippoObjectPurpose" value="QUOTE" /> <property name="shippoAddressFromId" expression="json-eval($.shippoAddressFromId)" /> <property name="shippoParcelId" expression="json-eval($.shippoParcelId)" /> <!-- Common properties --> <property name="index" value="0" scope="operation" /> <!-- Retrieve unfulfilled orders from Shopify. --> <shopify.init> <accessToken>{$ctx:shopifyAccessToken}</accessToken> <apiUrl>{$ctx:shopifyApiUrl}</apiUrl> <format>{$ctx:shopifyFormat}</format> </shopify.init> <shopify.listOrders> <updatedAfter>{$ctx:updatedAfter}</updatedAfter> <limit>{$ctx:limit}</limit> <updatedBefore>{$ctx:updatedBefore}</updatedBefore> <sinceId>{$ctx:sinceId}</sinceId> <createdAfter>{$ctx:createdAfter}</createdAfter> <status>{$ctx:status}</status> <page>{$ctx:page}</page> <financialStatus>{$ctx:financialStatus}</financialStatus> <fulfillmentStatus>{$ctx:shopifyFulfillmentStatus}</fulfillmentStatus> <importedBefore>{$ctx:importedBefore}</importedBefore> <createdBefore>{$ctx:createdBefore}</createdBefore> <importedAfter>{$ctx:importedAfter}</importedAfter> <fields>{$ctx:fields}</fields> </shopify.listOrders> <property name="shopifyListOrdersStatusCode" expression="$axis2:HTTP_SC" /> <!-- Check the http status code for the listOrders method to proceed. --> <filter source="get-property('shopifyListOrdersStatusCode') != 200" regex="true"> <then> <!-- Error in listing unfulfilled orders. --> <property name="message" expression="//errors" /> <property name="message" expression="fn:concat('Error: ', get-property('message'))" /> <!-- Response generator for the listOrders error response. --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="shopify_listOrders" /> <with-param name="id" value="{$ctx:id}" /> <with-param name="status" value="Error" /> <with-param name="message" value="{$ctx:message}" /> </call-template> <loopback /> </then> </filter> <property name="unfulfilledOrderCount" expression="count(//order)" scope="operation" /> <!-- Check if there are orders to be fulfilled, if not return an error response to the user. --> <filter xpath="0 = get-property('operation', 'unfulfilledOrderCount')"> <then> <!-- Successful response --> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="shopify_listOrders" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="There are no orders to be fulfilled" /> </call-template> <loopback /> </then> </filter> <iterate id="candidatesIterator" expression="//order" sequential="true" continueParent="true" preservePayload="true"> <target> <sequence> <property name="shopifyOrderId" expression="//order/id[@type='integer']/text()" /> <property name="shopifyCountry" expression="//shipping-address[@type='Address']/country-code/text()" /> <!-- Check the avaiability of the Shipping Address country to proceed, note that this is mandatory to crete the address_to in Shippo --> <filter source="boolean(get-property('shopifyCountry'))" regex="true"> <else> <!-- Missing country value in the shipping address. --> <property name="message" expression="fn:concat('Shipping address Country is missing for the order with order ID:', get-property('shopifyOrderId'), ', Order fulfillment failed')" /> <call-template target="responseHandlerTemplate"> <with-param name="activity" value="shopify_listOrders" /> <with-param name="status" value="Skipped" /> <with-param name="message" value="{$ctx:message}" /> </call-template> </else> <then> <property name="shopifyAddressOne" expression="//shipping-address[@type='Address']/address1/text()" /> <property name="shopifyAddressTwo" expression="//shipping-address[@type='Address']/address2/text()" /> <property name="shopifyCity" expression="//shipping-address[@type='Address']/city/text()" /> <property name="shopifyCompany" expression="//shipping-address[@type='Address']/company/text()" /> <property name="shopifyPhone" expression="//shipping-address[@type='Address']/phone/text()" /> <property name="shopifyProvince" expression="//shipping-address[@type='Address']/province/text()" /> <property name="shopifyZip" expression="//shipping-address[@type='Address']/zip/text()" /> <property name="shopifyName" expression="//shipping-address[@type='Address']/name/text()" /> <call-template target="shopify-createShipmentsInShippo"> ( <with-param name="shopifyApiUrl" value="{$ctx:shopifyApiUrl}" /> | <with-param name="shopifyAccessToken" value="{$ctx:shopifyAccessToken}" /> | <with-param name="shopifyOrderId" value="{$ctx:shopifyOrderId}" /> | <with-param name="shopifyAddressOne" value="{$ctx:shopifyAddressOne}" /> | <with-param name="shopifyAddressTwo" value="{$ctx:shopifyAddressTwo}" /> | <with-param name="shopifyCity" value="{$ctx:shopifyCity}" /> | <with-param name="shopifyCompany" value="{$ctx:shopifyCompany}" /> | <with-param name="shopifyPhone" value="{$ctx:shopifyPhone}" /> | <with-param name="shopifyProvince" value="{$ctx:shopifyProvince}" /> | <with-param name="shopifyZip" value="{$ctx:shopifyZip}" /> | <with-param name="shopifyName" value="{$ctx:shopifyName}" /> | <with-param name="shopifyCountry" value="{$ctx:shopifyCountry}" /> | <with-param name="shippoApiUrl" value="{$ctx:shippoApiUrl}" /> | <with-param name="shippoUsername" value="{$ctx:shippoUsername}" /> | <with-param name="shippoPassword" value="{$ctx:shippoPassword}" /> | <with-param name="shippoAddressFromId" value="{$ctx:shippoAddressFromId}" /> | <with-param name="shippoParcelId" value="{$ctx:shippoParcelId}" /> ) * </call-template> </then> </filter> <!-- END: Check the availability of the Shipping Address country. --> <property name="index" expression="get-property('operation','index') + 1" scope="operation" /> </sequence> </target> </iterate> <filter xpath="get-property('operation', 'index') = get-property('operation', 'unfulfilledOrderCount')"> <then> <loopback /> </then> </filter> </inSequence> <outSequence> <property name="messageType" value="application/json" scope="axis2" /> <!-- Generate the chained response of all the API calls in createShipment. --> <payloadFactory media-type="json"> <format> { "Response":{ "process":"shopify-createShipments", "activityResponse":[$1] } } </format> <args> <arg evaluator="xml" expression="get-property('operation', 'responseString')" /> </args> </payloadFactory> <send /> </outSequence> </target> <description /> </proxy>
{ "shopifyApiUrl":"https://disnafirststore.myshopify.com", "shopifyAccessToken":"32841521265cb52bef980cff1da1e008", "shippoApiUrl":"https://api.goshippo.com", "shippoUsername":"apptest.dilruk@gmail.com", "shippoPassword":"1qaz2wsx@", "shippoAddressFromId":"56a6140c9aa74f3897c23aad88be0322", "shippoParcelId":"2b834212c8594d69b4b0b18331060e12" }
.