Project

belpost

0.0
A long-lived project that still receives updates
Gem for working with the 'Belpost' delivery service via API
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

 Project Readme

Belpost

Client for working with Belpochta API (Belpost).

Tests Gem Version

Installation

Add this line to your application's Gemfile:

gem 'belpost'

And then execute:

$ bundle install

Or install it yourself:

$ gem install belpost

Configuration

Configure the client to work with the Belpost API:

require 'belpost'

Belpost.configure do |config|
  config.jwt_token = 'your_jwt_token_from_belpost'
  config.base_url = 'https://api.belpost.by'
  config.timeout = 30 # Timeout in seconds (default 10)
end

You can also use environment variables:

BELPOST_JWT_TOKEN=your_jwt_token_from_belpost
BELPOST_BASE_URL=https://api.belpost.by
BELPOST_TIMEOUT=30

Usage

Creating a parcel

Basic example

client = Belpost::Client.new

parcel_data = {
  parcel: {
    type: "package",
    attachment_type: "products",
    measures: {
      weight: 12
    },
    departure: {
      country: "BY",
      place: "post_office"
    },
    arrival: {
      country: "BY",
      place: "post_office"
    }
  },
  addons: {
    declared_value: {
      currency: "BYN",
      value: 100
    },
    cash_on_delivery: {
      currency: "BYN",
      value: 10
    }
  },
  sender: {
    type: "legal_person",
    info: {
      organization_name: "LLC \"Company\"",
      taxpayer_number: "123456789",
      IBAN: "BY26BAPB30123418400100000000",
      BIC: "BAPBBY2X",
      bank: "JSC 'BELAGROPROMBANK'"
    },
    location: {
      code: "225212",
      region: "Brest",
      district: "Bereza",
      locality: {
        type: "city",
        name: "Bereza"
      },
      road: {
        type: "street",
        name: "Lenin"
      },
      building: "1",
      housing: "",
      apartment: ""
    },
    email: "test@example.com",
    phone: "375291234567"
  },
  recipient: {
    type: "natural_person",
    info: {
      first_name: "Ivan",
      second_name: "Ivanovich",
      last_name: "Ivanov"
    },
    location: {
      code: "231365",
      region: "Grodno",
      district: "Ivye",
      locality: {
        type: "village",
        name: "Dudy"
      },
      road: {
        type: "street",
        name: "Central"
      },
      building: "1",
      housing: "",
      apartment: ""
    },
    email: "",
    phone: "375291234567"
  }
}

response = client.create_parcel(parcel_data)
puts "Tracking code: #{response["data"]["parcel"]["s10code"]}"

Using ParcelBuilder

client = Belpost::Client.new

# Creating a domestic parcel
parcel_data = Belpost::Models::ParcelBuilder.new
  .with_type("package")
  .with_attachment_type("products")
  .with_weight(1500) # weight in grams
  .with_dimensions(300, 200, 100) # length, width, height in mm
  .to_country("BY")
  .with_declared_value(100)
  .with_cash_on_delivery(50)
  .add_service(:simple_notification)
  .add_service(:email_notification)
  .from_legal_person("LLC \"Company\"")
  .with_sender_details(
    taxpayer_number: "123456789",
    bank: "JSC 'BELAGROPROMBANK'",
    iban: "BY26BAPB30123418400100000000",
    bic: "BAPBBY2X"
  )
  .with_sender_location(
    postal_code: "225212",
    region: "Brest",
    district: "Bereza",
    locality_type: "city",
    locality_name: "Bereza",
    road_type: "street",
    road_name: "Lenin",
    building: "1"
  )
  .with_sender_contact(
    email: "test@example.com",
    phone: "375291234567"
  )
  .to_natural_person(
    first_name: "Ivan",
    last_name: "Ivanov", 
    second_name: "Ivanovich"
  )
  .with_recipient_location(
    postal_code: "231365",
    region: "Grodno",
    district: "Ivye",
    locality_type: "village",
    locality_name: "Dudy",
    road_type: "street",
    road_name: "Central",
    building: "1"
  )
  .with_recipient_contact(
    phone: "375291234567"
  )
  .build

response = client.create_parcel(parcel_data)
puts "Tracking code: #{response["data"]["parcel"]["s10code"]}"

Creating an international parcel with a customs declaration

client = Belpost::Client.new

# Creating a customs declaration
customs_declaration = Belpost::Models::CustomsDeclaration.new
customs_declaration.set_category("gift")
customs_declaration.set_price("USD", 50)
customs_declaration.add_item(
  {
    name: "Book",
    local: "Book",
    unit: {
      local: "PCS",
      en: "PCS"
    },
    count: 1,
    weight: 500,
    price: {
      currency: "USD",
      value: 50
    },
    country: "BY"
  }
)

# Creating an international parcel
parcel_data = Belpost::Models::ParcelBuilder.new
  .with_type("package")
  .with_attachment_type("products")
  .with_weight(500)
  .to_country("DE") # Germany
  .with_declared_value(50, "USD")
  .from_legal_person("LLC \"Company\"")
  .with_sender_location(
    postal_code: "225212",
    region: "Brest",
    district: "Bereza",
    locality_type: "city",
    locality_name: "Bereza",
    road_type: "street",
    road_name: "Lenin",
    building: "1"
  )
  .with_sender_contact(
    email: "test@example.com",
    phone: "375291234567"
  )
  .to_natural_person(
    first_name: "John",
    last_name: "Doe"
  )
  .with_foreign_recipient_location(
    postal_code: "10115",
    locality: "Berlin",
    address: "Unter den Linden 77"
  )
  .with_recipient_contact(
    phone: "4901234567890"
  )
  .with_customs_declaration(customs_declaration)
  .build

response = client.create_parcel(parcel_data)
puts "Tracking code: #{response["data"]["parcel"]["s10code"]}"

Getting a list of available countries

client = Belpost::Client.new
countries = client.fetch_available_countries
puts countries

Obtaining data for postal item validation

client = Belpost::Client.new
validation_data = client.validate_postal_delivery("BY")
puts validation_data

Obtaining HS codes for customs declaration

client = Belpost::Client.new
hs_codes = client.fetch_hs_codes
puts hs_codes

Searching for postal codes

client = Belpost::Client.new
postcodes = client.search_postcode(
  city: "Витебск",
  street: "Ильинского",
  building: "51/1",  # optional
  limit: 50          # optional, default: 50, range: 1-200
)
puts postcodes

Finding addresses by postal code

client = Belpost::Client.new
addresses = client.find_addresses_by_postcode("210001")
puts addresses

Finding a batch by ID

client = Belpost::Client.new
batch = client.find_batch_by_id(123)
puts batch

Listing and searching for batches

client = Belpost::Client.new

# Get all batches with default pagination
batches = client.list_batches
puts "Total batches: #{batches["total"]}"

# Get a specific page of results
batches = client.list_batches(page: 2)

# Filter by status (committed or uncommitted)
committed_batches = client.list_batches(status: "committed")
uncommitted_batches = client.list_batches(status: "uncommitted")

# Set the number of batches per page
batches = client.list_batches(per_page: 50)

# Search for a specific batch by number
batch_number = 12345
search_results = client.list_batches(search: batch_number)

# Combine multiple parameters
filtered_batches = client.list_batches(
  page: 2,
  status: "committed",
  per_page: 25,
  search: 12345
)

Generating address labels for a batch

client = Belpost::Client.new

# Generate address labels for a batch with ID 17292
documents = client.generate_batch_blanks(17292)
puts documents

# The response will contain document information with the following structure:
# {
#   "documents" => {
#     "id" => 8660,
#     "list_id" => 17292,
#     "status" => "processing",
#     "path" => nil,
#     "expire_at" => nil,
#     "created_at" => "2024-11-06 13:18:59",
#     "updated_at" => "2024-11-06 16:03:36",
#     "name" => "Партия (заказ) №91"
#   }
# }

Note: Address labels can only be generated for batches with the "In processing" status. When you make this request, all address labels for shipments within the batch will be regenerated. Label generation is available if the batch has the "has_documents_label" flag set to false.

If the batch has the "is_partial_receipt" flag set to true and contains shipments with attachments, a PS112e form with attachment descriptions will be included in the response ZIP archive.

Committing a batch

After a batch has been created and filled with items, you can commit it to change its status from "uncommitted" (or "В обработке") to "committed" (or "Сформирована").

# Commit a batch with ID 19217
result = client.commit_batch(19217)
puts "New status: #{result["status"]}" # => "committed"

Note: You can only commit a batch that is currently uncommitted, has items, and includes contents if the batch has the "is_partial_receipt" flag set to true.

Downloading batch documents

client = Belpost::Client.new

# Generate address labels for a batch with ID 17292
documents = client.generate_batch_blanks(17292)
puts documents

# The response will contain document information with the following structure:
# {
#   "documents" => {
#     "id" => 8660,
#     "list_id" => 17292,
#     "status" => "processing",
#     "path" => nil,
#     "expire_at" => nil,
#     "created_at" => "2024-11-06 13:18:59",
#     "updated_at" => "2024-11-06 16:03:36",
#     "name" => "Партия (заказ) №91"
#   }
# }

Error handling

The client may throw the following exceptions:

  • Belpost::ConfigurationError - configuration error
  • Belpost::ValidationError - data validation error
  • Belpost::ApiError - basic API error
  • Belpost::AuthenticationError - authentication error
  • Belpost::InvalidRequestError - request error
  • Belpost::RateLimitError - request limit exceeded
  • Belpost::ServerError - server error
  • Belpost::NetworkError - network error
  • Belpost::RequestError - request timeout

Example of error handling:

begin
  client = Belpost::Client.new
  response = client.create_parcel(parcel_data)
rescue Belpost::ValidationError => e
  puts "Validation error: #{e.message}"
rescue Belpost::AuthenticationError => e
  puts "Authentication error: #{e.message}"
rescue Belpost::ApiError => e
  puts "API error: #{e.message}"
end

Documentation

Full documentation on the Belpost API is available in the official documentation.

Development

After cloning the repository, run bin/setup to install dependencies. Then run rake spec to run tests. You can also run bin/console for an interactive REPL that allows you to experiment.

Setting up test environment

For running tests, the gem uses environment variables that can be configured in different ways:

  1. Copy the .env.test.example file to .env.test and adjust the values:

    cp .env.test.example .env.test
    
  2. The test suite will automatically use these values, or fall back to default test values if not provided.

  3. For CI environments, the necessary environment variables are already configured in the GitHub workflow files.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Continuous Integration (CI/CD)

The project is set up to use GitHub Actions for continuous integration (CI) and continuous delivery (CD):

  1. Testing: Every push and pull request to the master branch automatically runs tests on various Ruby versions.
  2. Release: When a tag starting with v is created (e.g., v0.1.0), the gem will be automatically published to RubyGems.

For more detailed information about the release process, see the RELEASING.md file.

Contributing

For information on how to contribute to the project, please see CONTRIBUTING.md.

License

The gem is available as open source under the terms of the MIT License.