Skip to content
This repository was archived by the owner on Mar 4, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
activesupport (7.0.6)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
adsf (1.2.0)
Expand All @@ -24,7 +24,7 @@ GEM
nio4r (>= 0.5.0)
coderay (1.1.0)
colored (1.2)
concurrent-ruby (1.0.5)
concurrent-ruby (1.2.2)
cri (2.7.0)
colored (~> 1.2)
em-websocket (0.5.0)
Expand Down Expand Up @@ -65,7 +65,7 @@ GEM
guard-pow (2.0.0)
guard (~> 2.0)
http_parser.rb (0.5.3)
i18n (0.9.1)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
json (2.3.0)
kramdown (2.4.0)
Expand All @@ -78,7 +78,7 @@ GEM
lumberjack (1.0.4)
method_source (0.8.2)
mini_portile2 (2.8.0)
minitest (5.11.1)
minitest (5.19.0)
multi_json (1.15.0)
multipart-post (2.1.1)
nanoc (3.8.0)
Expand Down Expand Up @@ -118,10 +118,9 @@ GEM
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
thor (0.18.1)
thread_safe (0.3.6)
timers (1.1.0)
tzinfo (1.2.4)
thread_safe (~> 0.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uglifier (2.7.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
Expand Down
47 changes: 47 additions & 0 deletions content/guides/accounts-synchronization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Accounts synchronization

1. TOC
{:toc}

## Preface

With this endpoint you can import all accounts who wants to publish their listings on your website. As a next step you can sign a contracts with these accounts if needed, create accounts on your side, etc.

## Get all accounts

You can find detailed specification in [Swagger documentation](https://demo.platforms.bookingsync.com/api-docs/index.html)

> This endpoint does not support pagination.

> Don't forget to disable accounts and their rentals if you don't see them anymore in current list

> We suggest to do synchronization of accounts every 12-24 hours.

~~~ruby
token = "<YOUR_TOKEN>"
api_url = "<API_URL>"
media_type = "application/vnd.api+json"
options = {
headers: {
"User-Agent" => "Api client",
"Accept" => media_type,
"Content-Type" => media_type,
"Authorization" => "Bearer #{token}"
}
}
request = Excon.new(URI.join(api_url, "/api/ota/v1/accounts").to_s, options)
response = request.request({ method: :get })

response.status

already_import_accounts_ids = get_imported_accounts_ids # get an array of already imported accounts, Ex result: [1,2,3]

json = JSON.parse(response.body)
json["data"].each do |account|
# Import accounts
import_account(account["id"], account["attributes"]["name"])
end

accounts_for_disabling = already_import_accounts_ids - json["data"].pluck("id")
disable_accounts_and_rentals(accounts_for_disabling) # Disable accounts
~~~
40 changes: 40 additions & 0 deletions content/guides/api-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Smily Channel API Overview

1. TOC
{:toc}

## What is Smily Channel API?

Channel API allows you to sync Smily properties to your own website (aka Channel). You can choose which properties or accounts to display or not, get rentals availabilities, book rentals and collect payments.

## Who is Smily Channel API for?

TODO

## Why would I use Smily Channel API?

This API was developed to make integration with our partners easier. If you want to look at all features of our system, please refer to [BookingSync Universe API ](https://developers.bookingsync.com/).

## How the Smily Channel API works?

In general, there are 3 steps you have to do:

1. Manage accounts that are ready to publish their listings on your website - regularly refresh the list and onboard them.
2. Manage rentals of onboarded accounts - regularly refresh descriptions, prices and availabilities.
3. Manage bookings. This step depends on how you want to process the payments - on your side, or on our side.


The API is fully [JSONAPI 1.0 compliant](http://jsonapi.org).

1. Get accounts
2. approve/reject them
3. sign contracts/register/oboarding/etc
4. get rentals of approved accounts
4.1 Get rentals info (refresh once a day)
4.2 Get rentals availabilities (refresh every hour)
4.3 Get rentals prices (every 12 hours)
4.4 Remove unpublished rentals
5. Booking creation:
5.1 Create quote to confirm price and availability.
5.2 Create booking with price not less than in quote response
5.4 Create payment to confirm booking
86 changes: 86 additions & 0 deletions content/guides/booking-cancelation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Booking cancelation

1. TOC
{:toc}

## Preface

Bookings could be canceled according to the rental's cancelation policy. You can get cancelation policy from rental's enpoint, usually it looks like:

~~~js
"cancelation-policy-items": [
{
"id": "1",
"penalty-percentage": 10,
"eligible-days": 2,
"message-translations": {
"en": "Lorem ipsum",
"fr": "Lorem ipsum"
}
}
]
~~~

In this case you can cancel the booking not later than 2 days before check-in. And penalty will be 10% from the booking final price.

## Booking cancelation

You can find detailed specification in [Swagger documentation](https://demo.platforms.bookingsync.com/api-docs/index.html)

~~~ruby
token = "<YOUR_TOKEN>"
api_url = "<API_URL>"
media_type = "application/vnd.api+json"
options = {
headers: {
"User-Agent" => "Api client",
"Accept" => media_type,
"Content-Type" => media_type,
"Authorization" => "Bearer #{token}"
}
}

request = Excon.new(URI.join(api_url, "/bookings/#{booking_id}/cancel").to_s, options)
payload = {
data: {
attributes: {
"cancelation-reason": "canceled_by_traveller_other",
"cancelation-description": "health concern",
"channel-cancelation-cost": "80.0",
"currency": "USD"
},
type: "bookings",
id: booking_id
}
}
response = request.request({
method: :patch,
body: payload.to_json
})

json = JSON.parse(response.body)
if response.status == 200
booking_canceled_at = json["data"]["attributes"]["canceled-at"]
# update new booking information
else
handle_errors(json)
end
~~~

In some cases you can get a 422 error. Response will look like this:

~~~js
{
"errors" => [
{
"code" => "100",
"detail" => "currency - provided currency for cancelation must be the same as the one from booking",
"source" => {
"pointer" => "/data/attributes/currency"
},
"status" => "422",
"title" => "provided currency for cancelation must be the same as the one from booking"
}
]
}
~~~
160 changes: 160 additions & 0 deletions content/guides/create-a-booking-payments-on-partner-side.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Create a booking and handle payment on partner's side

1. TOC
{:toc}

## Preface

There are 2 ways of handling payments: using Smily payment gateway or process payment on partner side. This document explains second option - how to handle payments on partner's side.

## Create a quote

Before creating a booking, you have to confirm the price and availability of rental. To do that, you have to create a quote.

~~~ruby
token = "<YOUR_TOKEN>"
api_url = "<API_URL>"
media_type = "application/vnd.api+json"
options = {
headers: {
"User-Agent" => "Api client",
"Accept" => media_type,
"Content-Type" => media_type,
"Authorization" => "Bearer #{token}"
}
}

request = Excon.new(URI.join(api_url, "/api/ota/v1/quotes").to_s, options)
payload = {
data: {
attributes: {
"start-at": "2023-08-04",
"end-at": "2023-08-11",
"adults": 20,
"children": 0,
"rental-id": 428
},
type: "quotes"
}
}
response = request.request({
method: :post,
body: payload.to_json
})

json = JSON.parse(response.body)
if response.status == 201
price = json["data"]["attributes"]["final-price"]
booking_url = json["data"]["attributes"]["booking-url"]
# Now you can create booking via API or redirect user to booking_url
else
handle_errors(json)
end
~~~

## Create a booking

Once you have successfully created a Quote, you can make a booking request. To do that you have to provide all information about the client, dates, rental ID and price.

> Price should not be less than `final-price` you got from Quote request

~~~ruby
token = "<YOUR_TOKEN>"
api_url = "<API_URL>"
media_type = "application/vnd.api+json"
options = {
headers: {
"User-Agent" => "Api client",
"Accept" => media_type,
"Content-Type" => media_type,
"Authorization" => "Bearer #{token}",
"Idempotency-Key" => get_order_uuid # optional but useful header, read comments below
}
}

request = Excon.new(URI.join(api_url, "/api/ota/v1/bookings").to_s, options)
payload = {
data: {
attributes: {
"start-at": "2020-09-04T16:00:00.000Z", # "2020-09-04" works too
"end-at": "2020-09-11T10:00:00.000Z", # "2020-09-11" works too
"adults": 2,
"children": 1,
"final-price": "176.0",
"currency": "EUR",
"rental-id": 1,
"client-first-name": "Rich",
"client-last-name": "Piana",
"client-email": "rich@piana.com",
"client-phone-number": "123123123",
"client-country-code": "US"
},
type: "quotes"
}
}
response = request.request({
method: :post,
body: payload.to_json
})

json = JSON.parse(response.body)
if response.status == 201
booking_id = json["data"]["id"]
# save this booking id
else
handle_errors(json)
end
~~~

TODO: confirm it
If created booking has field `tentative-expires-at`, it means it could be canceled automatically if you won't create any payment. So, as a next step, we have to create payments.

We recommend to always set `Idempotency-Key` header. This header allow to avoid duplicates creation. For example if you tried to create a booking and because of network connection or some other error you could not get response, you can safely retry your request and get the correct response, because for a given key, every success response will be cached for 6 hours.
We recommend to generate UUID for each order and use it in `Idempotency-Key` header.

## Create a payment

When you handled payment for the booking, you have to notify our us about that. Otherwise booking could be canceled.

~~~ruby
token = "<YOUR_TOKEN>"
api_url = "<API_URL>"
media_type = "application/vnd.api+json"
options = {
headers: {
"User-Agent" => "Api client",
"Accept" => media_type,
"Content-Type" => media_type,
"Authorization" => "Bearer #{token}",
"Idempotency-Key" => get_payment_uuid # optional but useful header, read comments below
}
}

request = Excon.new(URI.join(api_url, "/api/ota/v1/payments").to_s, options)
payload = {
data: {
attributes: {
"amount": "100.0",
"currency": "EUR",
"paid-at": "2020-09-10T05:30:18.321Z",
"kind": "credit-card",
"booking-id": booking_id
},
type: "payments"
}
}
response = request.request({
method: :post,
body: payload.to_json
})

json = JSON.parse(response.body)
if response.status == 201
booking_id = json["data"]["id"]
# save this booking id
else
handle_errors(json)
end
~~~

Payments endpoint also support `Idempotency-Key` header. To ensure idempotent writes and frictionless integration, it is highly recommended to provide `Idempotency-Key` header. For a given key, every success response will be cached for 6 hours. Thanks to that, you can safely retry write operation.
Loading