There are two ways to add your logistics to AlterCPA – through hacks or the standard delivery service. The hack option is more powerful, more serious, but requires an understanding of how the platform works. But through the standard delivery service, you can link everything using three URLs and any convenient implementation in your favorite language without interfering with the code of the system itself. It means such a thing is also available in the free AlterCPA Moe.
We have already discussed the general principles of delivery services in the article “Improved logistics for AlterCPA and CPAmoe“, I recommend rereading it and remembering how and why we play couriers. For advanced techies, I recommend studying delivery service documentation in hacks.
How to setup the standard delivery service?
Logistics is configured in the “Delivery” section of the company management. You need to add a new service with the “Встроенная” or “Generic” type. It has a number of useful options:
- The delivery service must be activated after configuration. If you check the “Default” box, it will be assigned to all incoming leads.
- If you have a tracking URL, enable automatic transfer of orders by delivery status. So the CRM itself will drag the lead to “Paid” or “Return” if logistics issues the appropriate status.
- The price and cost of delivery are set fixed, it does not support dynamic determination of price tags before sending using a markup. The percentage for settlement and cash services in the cost price is calculated from the order price for the buyer, and not from the cost price.
- With active tracking, you can enable automatic sending of orders to the delivery call center if they are stuck in one of the statuses for several days.
- If you have a sending link, you can also automatically add orders to the courier service. This is especially convenient when working with fulfillment: just specify the status, where to pick up the parcel from and where to transfer it. I recommend adding a nested status for sending errors – dragging them into the return is questionable.
- The main feature of the built-in delivery service is the ability to specify three integration links, which will be discussed in this article. Read and automate!
Important point: the automatic sending can be configured only after you specify the link for sending parcels. While the service is not active, you can enter any nonsense there, save, go back and make the necessary settings.
What is the convenience of using it?
It was conceived as a stub for a manually processed courier service. Its main advantage is three magic links for web hooks:
- Tracking URL allows you to implement tracking of the parcel at all stages of the route. It should receive the parcel’s tracking code and give a history of its statuses.
- Sending URL allows you to create parcels on the logistics service side. Order data is sent to it, and a tracking code is expected in response.
- Documents URL allows you to add label printing to parcels. It should give a link to files with documents.
Our path is simple: we create files on a third-party hosting, add links to them to the delivery service and get a ready-made automated logistics interface!
Features of links
All three links support macros. Using a macro, you need to enter the parcel’s tracking code or order ID in the tracking and documents links. Available macros:
{code}
or{track}
– parcel tracking code received upon shipment.{order}
– order identifier on the CRM side.{track:xxx}
– tracking parameter namedxxx
received upon shipment.
Additional parameters can be used, for example, to specify an internal ID in a specific delivery service or any other actions. They are extracted from the meta
field when sending a parcel.
Parcel tracking
Parcel statuses are polled twice a day automatically. The operator can check the status history manually from the order page. Upon receipt of certain statuses, the parcel is automatically transferred to the “Delivered”, “Paid” and “Return” statuses.
At the specified URL, your logistics service should issue a status history in JSON format as a simple array of objects, each of which corresponds to its own status. Compliance with the structure specified below is strictly mandatory.
The status object can contain the following fields:
status
— mandatory field with symbolic delivery status, list below.time
— mandatory field with the time of the status occurrence in UNIX timestamp format.country
— symbolic ISO country code.zip
— index or ZIP code.city
— city or locality.comment
— arbitrary comment on the status.
The status
field can contain one of the following values:
wait
— the parcel is awaiting shipment.transfer
— the parcel is in transit, the status is changed to “Delivery”.problem
— problems arose during parcel delivery. problems, supplier response required.delivered
— the parcel has arrived at the destination, the status is changed to “Delivered”.paid
— the parcel has been successfully redeemed, the status is changed to “Paid”.return
— the parcel has been returned or disposed of, the status is changed to “Return”.comment
— an arbitrary comment that does not change the status of the parcel.
An example of a function response might look like this:
[ { "status":"delivered", "time":1234567890, "zip":"AB1234", "city":"Neverland" }, { "status":"paid", "time":1234567891, "comment":"Awesome!" } ]
Your link to check the history of parcel statuses may look like this:
https://yourlogistics/track.php?code={track}
The PHP handler file can be implemented like this:
<?php // Prepare the code and URL $code = filter_var( $_GET['code'] ); $url = "https://someotherlogiscits/track/$code" $status = []; // Status to status table $s2s = [ 123 => 'delivered', 234 => 'return', 345 => 'paid', 456 => 'problem' ]; // Converts service status to CRM status // Fetch the info from the service $reply = file_get_contents( $url ); $result = json_decode( $reply, true ); // Walt through statuses in tracking array foreach ( $result['tracking'] as $t ) { $stage = [ 'status' => $s2s[$t['status']] ?: 'comment', 'time' => strtotime($t['time']) ]; if ( $t['location'] ) $stage['city'] = $t['location']; if ( $t['geo'] ) $stage['country'] = $t['geo']; if ( $t['messages'] ) $stage['comment'] = implode( ' ', $t['messages'] ); $status[] = $stage; } // Show the results echo json_encode( $status );
In this file, you need to prepare an array of statuses in accordance with the AlterCPA standard. It is convenient to use $s2s
to match the status on the delivery service side with the AlterCPA status. If there are no statuses or an error occurred, simply issue an empty array.
Sending a parcel
Parcels can be sent automatically according to a schedule or manually from the order card. CRM sends a POST request in JSON format with order data to the specified link, so it is not necessary to use any macros in the sending link.
At the specified URL, your logistics service should receive the order in JSON, create a parcel and respond in JSON format with the following fields:
status
– the shipment status, can beok
orerror
, mandatory field.error
– the error code for theerror
status, which will be stored in the shipment log.message
– the text message about the error, which will be added to the order changes.track
– the tracking code for checking the parcel and printing documents, mandatory field.price
– the cost of shipping the parcel, which will be recorded in the cost of shipping the order.meta
– additional fields that need to be added to the order for further status checking, such as a key or an additional code. Specified as an associative array field-value.
Order as an associative array with the fields:
id
– order identifier on the CRM side.name
– buyer’s name.phone
– buyer’s phone number in international format, numbers only.email
– buyer’s email.country
– ISO code of the buyer’s country, two letters, lowercase.zip
,region
,city
,street
,address
– buyer’s address elements: postal code, region, city, street, house.created
,approved
– time of order creation and confirmation in UNIX timestamp format.price
– the total price of the order, which includes the price of goods, delivery and markup.delivery
– the cost of delivery of the order as part of the price.markup
– the cost of an additional markup or discount as part of the price.currency
– three-letter ISO code of the order currency.meta
– additional order fields, depend on the settings of the custom fields of the offer.param
– internal parameters of the offer.items
– goods in the order.
The items
field is represented by an array that contains the goods included in the order. The array contains at least one element with the main product. Each product is represented by the following fields:
id
– internal identifier of the product item (offer or product)name
– product name for the buyer.sku
– product article number or offer ID.price
– price per unit of product.count
– number of products in the order.param
– product parameters from the CRM, it is optimal to use them to transfer the dimensions and weight of the product.
Example of a request with the full composition of the order:
{ "id": 1707, "name": "John Doe", "phone": "79876543210", "email": "john.doe@gmail.com", "country": "ru", "zip": "127000", "region": "Moscow City", "city": "Moscow", "street": "Bolshaya Lubyanka", "address": "house 1", "created": 1658581026, "approved": 1658678174, "price": 1550, "delivery": 500, "markup": 50, "currency": "rub", "meta": { "prepaid": 1, "prepay": 550 }, "param": { "crm-id": "4132", "ref-code": "10" }, "items": [ { "id": 1, "name": "Cube Cat", "article": "cube", "price": 600, "count": 1, "param": { "weight": 100, "width": 21, "height": 16, "depth": 20 } }, { "id": 2, "name": "Green Hat", "article": "hat", "price": 400, "count": 1, "param": [] } ] }
The link for sending parcels may look like this, no macros needed:
https://yourlogistics/send.php?token=secret
Example of a handler file for sending orders:
<?php // Get the lead data and make some magic $data = json_decode( file_get_contents('php://input'), true ); $result = makesomemagic( $data ); // Process the result if ( $result['success'] ) { $code = trim( $result['track'] ); $info = [ 'status' => 'ok', 'track' => $code, 'meta' => [ 'mode' => 'fullfillment' ] ]; } else $info = [ 'status' => 'error', 'message' => $result['error'] ]; // Show the result info echo json_encode( $info );
How exactly the magic that connects your file with the logistics service will work is up to you. It is recommended to add some kind of authorization to the file itself, for example, use an API token. It is mandatory to issue a tracking code at the output.
Printing documents
Document printing works individually in the order card or in bulk in the “Orders” section. When processing individually, the operator clicks the “Documents” button and opens the file for printing. When working in bulk, the service makes a separate request for each order, downloads the files and forms a ready-made archive from them.
At the specified URL, your logistics service should issue a response in JSON format with two fields: status
and url
. The status
field should contain ok
in case of successful formation of the document package and error
in case of an error. The url
field must contain a link to the document or document package that will be provided to the user.
An example of a successful function response may look like this:
{ "status": "ok", "url": "https://yourlogistics/docs/abcdef123456.zip" }
The response must contain a link to the compiled document file. Your service must prepare the document itself upon request, put it in a folder as a compiled file, and provide a link to it.
Your link to print documents may look like this:
https://yourlogistics/docs.php?code={track}
Then the PHP handler file may look like this:
<?php // Prepare the code and URL $code = filter_var( $_GET['code'] ); $uid = md5( $code . 'secret' ); $url = "https://yourlogiscits/docs/$uid.pdf" if (!file_exists( "docs/$uid.pdf" )) { $data = somemagictoprint( $code ); file_put_contents( "docs/$uid.pdf", $data ); } // Show the results echo json_encode([ 'status' => 'ok', 'url' => $url ]);
In this file, the somemagictoprint
function should create the document content itself. It will place it in a file with an encoded name and provide a link to it. The specific implementation depends on the delivery service.
Brief summary
Some final tips for creating your own logistics service:
- The simplest implementation option is to create a “wrapper” over the functions of the logistics service. Usually, it has the functions of sending, checking the status and printing documents.
- Try to save data on parcels and requests in some kind of journal. It is best to maintain a separate database of parcels and their statuses, extracting data from the database, rather than proxying them to the delivery service.
- Don’t forget to provide the actual delivery price when adding an order to accurately estimate the costs of your logistics.
- Be sure to organize all three functions – sending, documents and tracking. This will cover all logistics needs.
- Creating a full-fledged delivery service via hacks is still better.
Good luck with connecting logistics services!