[!WARNING] >Important: This gem is in the early stages of development and it is shared as it is. Any support for development or feedback is welcome; please check the Contributing section for more information.
CDEK (СДЭК) is a big logistics company in Russia, that provides a wide range of delivery services for businesses and individuals. The CDEK API allows developers to integrate CDEK's services into their applications, enabling functionalities such as order creation, tracking, tariff calculation, location data retrieval, and webhook management.
The cdek_api_client gem offers a clean and robust interface to interact with the CDEK API, ensuring maintainable code with proper validations. This gem supports the following features:
- Order Management: Creating, tracking, updating, canceling, deleting, and retrieving orders by various identifiers
- Tariff Calculation: Calculating single tariffs, tariff lists, and enhanced tariff calculations with services
- Location Services: Retrieving cities, regions, postal codes, delivery offices, and coordinates
- Print/Documents: Creating and retrieving barcodes, invoices, and other printable documents (PDF)
- Courier Services: Managing delivery agreements and intake requests for courier pickup
- Payment Services: Retrieving payment information, check data, and payment registries
- Webhook Management: Registering, listing, managing, and deleting webhooks for real-time notifications
Add this line to your application's Gemfile:
gem 'cdek_api_client'And then execute:
bundle installOr install it yourself as:
gem install cdek_api_clientTo use the CDEK API Client, you need to initialize it with your CDEK API credentials (client ID and client secret):
require 'cdek_api_client'
client_id = 'your_client_id'
client_secret = 'your_client_secret'
client = CDEKApiClient::Client.new(client_id, client_secret)To create an order, you need to create the necessary entities (OrderData, Recipient, Sender, Package, and Item) and then pass them to the create_order method of the Order class:
recipient = CDEKApiClient::Entities::Recipient.new(
name: 'John Doe',
phones: [{ number: '+79000000000' }],
email: '[email protected]'
)
sender = CDEKApiClient::Entities::Sender.new(
name: 'Sender Name',
phones: [{ number: '+79000000001' }],
email: '[email protected]'
)
item = CDEKApiClient::Entities::Item.new(
name: 'Item 1',
ware_key: '00055',
payment: 1000,
cost: 1000,
weight: 500,
amount: 1
)
package = CDEKApiClient::Entities::Package.new(
number: '1',
weight: 500,
length: 10,
width: 10,
height: 10,
comment: 'Package 1',
items: [item]
)
order_data = CDEKApiClient::Entities::OrderData.new(
type: 1,
number: 'TEST123',
tariff_code: 1,
comment: 'Test order',
recipient: recipient,
sender: sender,
from_location: { code: 44 },
to_location: { code: 270 },
packages: [package]
)
order_client = CDEKApiClient::Order.new(client)
begin
order_response = order_client.create(order_data)
puts "Order created successfully: #{order_response}"
rescue => e
puts "Error creating order: #{e.message}"
endTo track an order, use the track method of the Order class with the order UUID:
order_uuid = 'order_uuid_from_created_order_response'
begin
tracking_info = order_client.track(order_uuid)
puts "Tracking info: #{tracking_info}"
rescue => e
puts "Error tracking order: #{e.message}"
endTo calculate the tariff, use the calculate method of the Tariff class with the necessary tariff data:
tariff_data = CDEKApiClient::Entities::TariffData.new(
type: 1,
currency: 'RUB',
from_location: { code: 44 },
to_location: { code: 137 },
packages: [{ weight: 500, length: 10, width: 10, height: 10 }]
)
tariff_client = CDEKApiClient::Tariff.new(client)
begin
tariff_response = tariff_client.calculate(tariff_data)
puts "Tariff calculated: #{tariff_response}"
rescue => e
puts "Error calculating tariff: #{e.message}"
endTo retrieve location data such as cities and regions supported by CDEK, use the cities and regions methods of the Location class:
location_client = CDEKApiClient::Location.new(client)
# Fetching cities
begin
cities = location_client.cities
puts "Cities: #{cities}"
rescue => e
puts "Error fetching cities: #{e.message}"
end
# Fetching regions
begin
regions = location_client.regions
puts "Regions: #{regions}"
rescue => e
puts "Error fetching regions: #{e.message}"
endWebhooks allow your application to receive real-time notifications about various events related to your shipments. To set up a webhook, register a URL where CDEK will send HTTP POST requests with event data:
webhook_client = CDEKApiClient::Webhook.new(client)
webhook_url = 'https://yourapp.com/webhooks/cdek'
begin
response = webhook_client.register(webhook_url, event_types: ['ORDER_STATUS', 'DELIVERY_STATUS'])
puts "Webhook registered: #{response}"
rescue => e
puts "Error registering webhook: #{e.message}"
endTo retrieve and delete registered webhooks:
# Fetching webhooks
begin
webhooks = webhook_client.get_webhooks
puts "Webhooks: #{webhooks}"
rescue => e
puts "Error fetching webhooks: #{e.message}"
end
# Deleting a webhook
webhook_id = 'webhook_id_to_delete'
begin
response = webhook_client.delete(webhook_id)
puts "Webhook deleted: #{response}"
rescue => e
puts "Error deleting webhook: #{e.message}"
endThe gem has pre-cached values and uses them by default. Users can override this behavior by fetching live data.
You can fetch cities, regions, offices, and postal code data directly from CDEK API.
# Fetching cities
begin
cities = location_client.cities(use_live_data: true)
rescue => e
puts "Error fetching cities: #{e.message}"
end
# Fetching regions
begin
regions = location_client.regions(use_live_data: true)
rescue => e
puts "Error fetching regions: #{e.message}"
end
# Fetching offices
begin
offices = location_client.offices(use_live_data: true)
rescue => e
puts "Error fetching offices: #{e.message}"
end
# Fetching postal codes for each city
begin
cities = location_client.cities(use_live_data: true)
rescue => e
puts "Error fetching postal codes: #{e.message}"
endThe gem provides comprehensive order management capabilities beyond basic creation and tracking:
# Delete an order
begin
response = client.order.delete('order-uuid')
puts "Order deleted: #{response}"
rescue => e
puts "Error deleting order: #{e.message}"
end
# Cancel an order (refusal)
begin
response = client.order.cancel('order-uuid')
puts "Order canceled: #{response}"
rescue => e
puts "Error canceling order: #{e.message}"
end
# Update an order
updated_order_data = # ... create updated order data
begin
response = client.order.update(updated_order_data)
puts "Order updated: #{response}"
rescue => e
puts "Error updating order: #{e.message}"
end
# Get order by UUID
begin
order_info = client.order.get('order-uuid')
puts "Order info: #{order_info}"
rescue => e
puts "Error getting order: #{e.message}"
end
# Get order by CDEK number
begin
order_info = client.order.get_by_cdek_number('123456789')
puts "Order info: #{order_info}"
rescue => e
puts "Error getting order: #{e.message}"
end
# Get order by IM number
begin
order_info = client.order.get_by_im_number('ORDER123')
puts "Order info: #{order_info}"
rescue => e
puts "Error getting order: #{e.message}"
end
# Create client return order
client_return_data = CDEKApiClient::Entities::OrderData.new(
type: 1,
tariff_code: 1,
packages: [package]
)
begin
response = client.order.create_client_return('original-order-uuid', client_return_data)
puts "Client return created: #{response}"
rescue => e
puts "Error creating client return: #{e.message}"
end
# Get intakes for an order
begin
intakes = client.order.get_intakes('order-uuid')
puts "Order intakes: #{intakes}"
rescue => e
puts "Error getting intakes: #{e.message}"
endGenerate and retrieve printable documents like barcodes and invoices:
# Create barcode
barcode_data = CDEKApiClient::Entities::Barcode.with_orders_uuid('order-uuid')
begin
response = client.print.create_barcode(barcode_data)
puts "Barcode created: #{response}"
rescue => e
puts "Error creating barcode: #{e.message}"
end
# Get barcode PDF
begin
pdf_content = client.print.get_barcode_pdf('barcode-uuid')
File.write('barcode.pdf', pdf_content)
rescue => e
puts "Error getting barcode PDF: #{e.message}"
end
# Create invoice
invoice_data = CDEKApiClient::Entities::Invoice.with_orders_uuid('order-uuid')
begin
response = client.print.create_invoice(invoice_data)
puts "Invoice created: #{response}"
rescue => e
puts "Error creating invoice: #{e.message}"
end
# Get invoice PDF
begin
pdf_content = client.print.get_invoice_pdf('invoice-uuid')
File.write('invoice.pdf', pdf_content)
rescue => e
puts "Error getting invoice PDF: #{e.message}"
endManage delivery agreements and courier intake requests:
# Create delivery agreement
agreement_data = CDEKApiClient::Entities::Agreement.new(
cdek_number: '123456789',
date: '2024-01-17',
time_from: '10:00',
time_to: '18:00',
comment: 'Delivery agreement'
)
begin
response = client.courier.create_agreement(agreement_data)
puts "Agreement created: #{response}"
rescue => e
puts "Error creating agreement: #{e.message}"
end
# Create courier intake request
intake_data = CDEKApiClient::Entities::Intakes.new(
cdek_number: '123456789',
intake_date: '2024-01-17',
intake_time_from: '10:00',
intake_time_to: '18:00',
name: 'Cargo description',
sender: { name: 'Sender Name', phones: [{ number: '+79001234567' }] },
from_location: { code: 44, address: 'Pickup Address' }
)
begin
response = client.courier.create_intake(intake_data)
puts "Intake created: #{response}"
rescue => e
puts "Error creating intake: #{e.message}"
end
# Delete intake request
begin
response = client.courier.delete_intake('intake-uuid')
puts "Intake deleted: #{response}"
rescue => e
puts "Error deleting intake: #{e.message}"
endRetrieve payment and check information:
# Get payments for a date
begin
payments = client.payment.get_payments('2024-01-17')
puts "Payments: #{payments}"
rescue => e
puts "Error getting payments: #{e.message}"
end
# Get checks
check_data = CDEKApiClient::Entities::Check.new(
cdek_number: '123456789',
date: '2024-01-17'
)
begin
checks = client.payment.get_checks(check_data.to_query_params)
puts "Checks: #{checks}"
rescue => e
puts "Error getting checks: #{e.message}"
end
# Get payment registries
begin
registries = client.payment.get_registries('2024-01-17')
puts "Registries: #{registries}"
rescue => e
puts "Error getting registries: #{e.message}"
endCalculate all available tariffs at once:
# Calculate tariff list
begin
tariffs = client.tariff.calculate_list(tariff_data)
puts "Available tariffs: #{tariffs}"
rescue => e
puts "Error calculating tariffs: #{e.message}"
endList and manage webhooks:
# List all webhooks
begin
webhooks = client.webhook.list_all
puts "All webhooks: #{webhooks}"
rescue => e
puts "Error listing webhooks: #{e.message}"
end
# Get specific webhook
begin
webhook = client.webhook.get('webhook-uuid')
puts "Webhook: #{webhook}"
rescue => e
puts "Error getting webhook: #{e.message}"
end
# Delete webhook by UUID
begin
response = client.webhook.delete('webhook-uuid')
puts "Webhook deleted: #{response}"
rescue => e
puts "Error deleting webhook: #{e.message}"
endRepresents the order data.
Attributes:
type(Integer, required): The type of the order.number(String, required): The order number.tariff_code(Integer, required): The tariff code.comment(String): The comment for the order.recipient(Recipient, required): The recipient details.sender(Sender, required): The sender details.from_location(Hash, required): The location details from where the order is shipped.to_location(Hash, required): The location details of where the order is shipped.services(Array): Additional services.packages(Array, required): List of packages.
Represents the recipient details.
Attributes:
name(String, required): The recipient's name.phones(Array, required): List of phone numbers.email(String, required): The recipient's email address.
Represents the sender's details.
Attributes:
name(String, required): The sender's name.phones(Array, required): List of phone numbers.email(String, required): The sender's email address.
Represents the package details.
Attributes:
number(String, required): The package number.weight(Integer, required): The weight of the package.length(Integer, required): The length of the package.width(Integer, required): The width of the package.height(Integer, required): The height of the package.comment(String): The comment for the package.items(Array, required): List of items in the package.
Represents the item details.
Attributes:
name(String, required): The name of the item.ware_key(String, required): The ware key of the item.payment(Integer, required): The payment value of the item.cost(Integer, required): The cost of the item.weight(Integer, required): The weight of the item.amount(Integer, required): The amount of the item.
Represents barcode creation data for printing.
Attributes:
orders(Array, required): List of orders for barcode generation.copy_count(Integer): Number of copies (default: 1).format(String): Print format (A4, A5, A6, A7).lang(String): Language code (RUS, ENG, DEU, ITA, TUR, CES, KOR, LIT, LAT).
Represents invoice creation data for printing.
Attributes:
orders(Array, required): List of orders for invoice generation.copy_count(Integer): Number of copies (default: 2).type(String): Invoice type (tpl_russia, tpl_china, tpl_armenia, tpl_english, tpl_italian, tpl_korean, tpl_latvian, tpl_lithuanian, tpl_german, tpl_turkish, tpl_czech, tpl_thailand, tpl_invoice).
Represents delivery agreement data.
Attributes:
cdek_number(String, required): CDEK order number.order_uuid(String): Order UUID.date(String, required): Delivery date.time_from(String, required): Start time.time_to(String, required): End time.comment(String): Comment.delivery_point(String): Delivery point code.to_location(Hash): Delivery location details.
Represents courier intake request data.
Attributes:
cdek_number(String): CDEK order number.order_uuid(String): Order UUID.intake_date(String, required): Intake date.intake_time_from(String, required): Start time.intake_time_to(String, required): End time.lunch_time_from(String): Lunch start time.lunch_time_to(String): Lunch end time.name(String, required): Cargo description.need_call(Boolean): Whether a call is needed.comment(String): Comment for courier.courier_power_of_attorney(Boolean): Courier needs power of attorney.courier_identity_card(Boolean): Courier needs identity card.sender(Hash, required): Sender contact information.from_location(Hash, required): Pickup location details.weight(Integer): Cargo weight in grams.length(Integer): Cargo length in cm.width(Integer): Cargo width in cm.height(Integer): Cargo height in cm.
Represents check query data.
Attributes:
order_uuid(String): Order UUID.cdek_number(String): CDEK order number.date(String): Date for check retrieval.
The gem includes a schema management system to keep API definitions up-to-date. The pull_cdek_schemas.rb script fetches the latest OpenAPI schemas from CDEK's documentation and organizes them.
# Run the schema pull script
ruby pull_cdek_schemas.rb
# This will:
# 1. Fetch API documentation from CDEK
# 2. Parse and organize schemas
# 3. Save to cdek_api_schemas.json
# 4. Display analysis of available endpointsThe script analyzes and organizes:
- 5 main API schemas from CDEK documentation
- 40 unique endpoints across all schemas
- Complete endpoint mapping with HTTP methods
- Metadata for each schema version
Run the schema pull script periodically to stay updated with the latest CDEK API changes:
# Add to cron or CI/CD pipeline
0 2 * * * cd /path/to/gem && ruby pull_cdek_schemas.rb- Restructure the codebase for better organization.
- Add mappings for CDEK internal codes.
- Add more API endpoints and data entities (Order management, Print/Documents, Courier services, Payment services).
- Implement schema management system.
- Add comprehensive documentation in English, Russian, and Tatar.
- Check all attributes for required and optional fields.
- Add documentation for all classes and methods.
- Refine entity validations.
- Add more comprehensive error handling.
- Add rate limiting support.
(See CHANGELOG.md)
Bug reports and pull requests are welcome on GitHub. This gem is used in a couple of projects and fulfills the requirements of those projects. If you have any suggestions or improvements, feel free to open an issue or a pull request.
The gem is available as open source under the terms of the MIT License.