Selling to an Existing Customer
The following documentation describes the setup, via API, of a sale to an existing customer only.
Sales to people unknown to the club, for example on an unauthenticated portal, follow a specific process.
INFO
Retrieve the corresponding scenario in the Bruno collection available here : Selling a product
CAUTION
It is essential that you are familiar with the terms defined in the glossary before reading this documentation.
WARNING
In the case of integration with a payment provider, it is important to perform this action only when the provider has validated the payment (and not to add payment intents).
High-Level Sales Process
The sales process for an existing contact is structured as follows:
- Identify the contact
- Retrieve the catalog corresponding to the contact
- Sell one or more products to the contact
- Verify the validity of the sale
- Validate the sale and record the payment
Identify the contact
Several pieces of information related to the contact targeted by the sale must be collected beforehand. This information will then be used when retrieving the catalog.
Retrieve the contact tags
Tags are listed as a table in the description of a contact object, under the property tags
.
GET `/{clientToken}/contacts/{contactId}`
GET `/demoapi/contacts/1234`
{
"@context":"/clienttoken/contexts/Contact",
"@id":"/clienttoken/contacts/1234",
"@type":"http://schema.org/Person",
"number":"4110012692",
// ...
"tags":[ // <-- liste des mots clés de l'utilisateur
{
"@id":"/clienttoken/contact_tags/416202",
"@type":"ContactTag",
"name":"abonnement", // <-- mot clé #1
"validFrom":"2019-05-07",
"validThrough":"2019-06-07"
},
{
"@id":"/clienttoken/contact_tags/416245",
"@type":"ContactTag",
"name":"vip", // <-- mot clé #2
"validFrom":"2019-05-07",
"validThrough":"2019-06-07"
}
],
// ...
}
Retrieve the customer's debt amount
WARNING
If a customer has a debt, it is very likely that the sale of a product will be refused, especially if it is a subscription product.
It is therefore imperative that you first ensure that the contact has no outstanding debt.
And if they do, invite them to settle their debt before proceeding further (see dedicated documentation).
GET `/{clientToken}/stats/accounts/contacts/:contactId`
GET `/demoapi/stats/accounts/contacts/1234`
{
// ...
"totalRealDebt": 10000, // <-- la valeur de la dette actuelle du contact
// ...
}
Retrieve the contact's catalog
Before starting a sale, it is necessary to list the products that the contact will be able to purchase. To do this, we will query the API by providing the sale context.
GET /{clientToken}/products
parameter | description | example |
---|---|---|
context[channel] | Sales channel, allowed values are club , web , terminal . The catalog may vary depending on the sales channel. | web |
context[club] | Club where the sale takes place. The catalog may vary depending on the club. | /clientToken/clubs/123 |
context[contactTag] | Contact's tags. The catalog may vary depending on the tags (e.g., 'VIP' or 'students'). | tagname |
context[debt] | Customer's debt (in cents). Some clubs restrict offers for customers with high debt. | 600 |
context[date] | Desired purchase date. Some products are not available year-round (e.g., back-to-school promotions). | 2019-04-15 |
type | Product category. It is possible to filter by category. Allowed values: subscription , subscription_option , badge , counter , package , countermark , test_session , regular | subscription see category mappings below |
page | Page number. The response contains 30 results per page, so pagination is supported. | 1 |
GET /{clientToken}/products?context[channel]=club&context[club]=/{clientToken}/clubs/123&context[contactTag][]=tag-1&context[contactTag][]=tag-2&context[contactId]=/{clientToken}/contacts/123&context[debt]=0&context[date]=2019-04-02&type=badge&page=1
Product Categories
Here are the existing categories:
subscription
: "subscription" productssubscription_option
: "subscription option" productsbadge
: "badge" productscounter
: "session booklets" productspackage
: packaged formula productscountermark
: "countermark" productstest_session
: "trial session" productsregular
: "shop" products
Selling one or more products to the contact
To start a sale, you must first create the cart that will hold the items you want to sell. Only a few basic pieces of information are needed at creation:
- The contact to whom the sale is made (
contactId
) - The club where the sale takes place (
clubId
,clubCode
)
Additional non-mandatory information can be provided at creation, but you will need to fill them in before final validation. This information will be used for invoicing:
- Additional contact information (
contactFamilyName
,contactGivenName
,contactNumber
) - Their address (
address
)
Creating the sale
Required information in the request:
parameter | description | example |
---|---|---|
contactId | Identifier of the contact targeted by the sale. This is an IRI, not just a numeric ID | /{clientToken}/contacts/12345 |
contactFamilyName | Contact's family name. | Doe |
contactGivenName | Contact's given name. | John |
contactNumber | Contact's number. | 423456 |
clubId | Club identifier in the form of an IRI | /{clientToken}/clubs/1 |
clubCode | Club code | ABCDE |
address[addressCountry] | Country | France |
address[addressLocality] | City | La Madeleine |
address[postalCode] | Postal code | 59110 |
address[streetAddress] | Address | 41 rue du Général de Gaulle |
POST `/{clientToken}/sales`
curl --location --globoff '/demoapi/sales' \
--data '{
"contactId": "/demoapi/contacts/1",
"contactFamilyName": "Doe",
"contactGivenName": "John",
"contactNumber": "1",
"clubId": "/demoapi/clubs/1",
"clubCode": "ABCDE",
"address": {
"addressCountry": "France",
"addressLocality": "La Madeleine",
"postalCode": "59110",
"streetAddress": "41 Rue du Général de Gaulle"
}
}'
{
"@context":"/clientToken/contexts/Sale",
"@id":"/clientToken/sales/373969", // <-- identifiant du panier
"@type":"Sale",
// ...
}
Adding an item to the cart
Some important details before adding an item to the cart.
Behaviors
Like any sales API, we have a catalog composed of products and a cart composed of items. On top of these classic notions are behaviors (behaviors
). They are responsible for the fitness business logic and the implications related to the purchase of various products (adding a subscription, crediting sessions, etc.).
Among these behaviors, some require details from the buyer (for example, buying a subscription requires a start date) and others require no details at all (for example, buying a water bottle or a towel involves no questions).
Refer to the documentation dedicated to product behaviors.
Selling a product
To sell a product, you need to add it to the cart. You will therefore need the identifier of the previously created cart. See cart creation.
Additionally, depending on the behaviors associated with the product, you will have to provide the expected details. Adding an item to the cart always goes through the same API endpoint; only the parameters vary depending on the behaviors.
POST /{clientToken}/sales/{saleId}/articles
parameter | description | example |
---|---|---|
offerId | Identifier of the product offer you want to sell. This is an IRI, not just a numeric ID. | /{clientToken}/offers/4321 |
implementation | (optional) details related to the behavior of the added product. See below for expected formats | |
implementations | (optional) details related to multiple behaviors of the product in case of packages. See below for expected formats |
Verify the validity of the sale
Some products in the sale may not be compatible with the contact. For example, an offer with an age restriction contradicted by the birth date. Adding it to the sale is not prevented since you might want to modify the contact later. However, if you do not check the sale’s integrity before submitting it, you risk errors.
WARNING: Warning: Settling (via a payment) an incompatible sale will charge the client’s account — the sale will not be settled (allocated): the club will have to redo the sale and allocate the payment.
GET /{client_token}/sales/{id}/check
This endpoint returns
204
The sale is valid
400
The sale contains errors
404
Invalid sale ID / sale not found
Validate the sale and record the payment
To validate a sale, you can either:
- Add the payment
- Defer the payment of the sale
Know the register and the amount to collect
Before recording a payment, some information to gather:
- amount to collect
- register on which to record the payment
To do this, simply query our cart.
GET /{clientToken}/sales/{saleId}
parameter | description | example |
---|---|---|
saleId | Cart identifier | 456 |
Example request
GET /{clientToken}/sales/1
{
// ...
"checkoutId": "/clientToken/checkouts/1234", // <-- identifiant de la caisse
"totalTI": 1100, // <-- montant à encaisser en centimes
// ...
}
Validate the sale by adding a payment
WARNING
Be careful, these calls create payments in the application, so it is important to understand that each call creates a payment with accounting links behind it.
For a €50 sale, if two API calls to "add a payment" of €50 are made, the client will have an overpayment of €50 which can be used to settle other purchases, even if the client has not actually paid €100. It is crucial to secure these calls properly.
When adding a payment, several pieces of information are required:
- The contact making the payment (
contactId
,contactFamilyName
,contactGivenName
,contactNumber
) - The club where the payment is made (
clubId
,clubCode
,checkout
) - The amount of the payment (
amount
) - The payment method, among:
CBWeb
, for an online card paymentcash
, for a cash paymentcheque
, for a cheque paymentcard
, for an on-site card payment
POST /{clientToken}/sales/{saleId}/payments
parameter | description | example |
---|---|---|
contactId | Identifier of the contact making the payment | /{clientToken}/contacts/456 |
contactFamilyName | Last name of the contact making the payment | Doe |
contactGivenName | First name of the contact making the payment | John |
contactNumber | Contact number of the contact making the payment | 98765 |
amount | Payment amount in cents | 4995 |
mean | Payment method | CBWeb |
clubId | Club identifier as an IRI | /{clientToken}/clubs/1 |
clubCode | Club code | ABCDE |
checkout | Club register identifier as an IRI | /{clientToken}/checkouts/2 |
paymentCards | Details of card payments, only if mean=card | |
paymentCards[][amount] | Card payment amount in cents (only if mean=card ) | 4995 |
paymentCards[][network] | Card network among CBGroup , AmEx , Payline , and External (only if mean=card ) | AmEx |
cheques | Details of cheque payments, only if mean=cheque | |
cheques[][amount] | Cheque amount in cents (only if mean=cheque ) | 4995 |
cheques[][bank] | Bank name (only if mean=cheque ) | Banque populaire |
cheques[][code] | Cheque number (only if mean=cheque ) | 1234567890 |
cheques[][depositDesiredDate] | Desired deposit date (only if mean=cheque ) | 2019-01-31 |
cheques[][owner] | Name of the cheque owner (only if mean=cheque ) | John Doe |
Example of a Web payment
curl '/{clientToken}/sales/1/payments'
{
"contactId": "/{clientToken}/contacts/1",
"contactFamilyName": "Doe",
"contactGivenName": "John",
"contactNumber": "1",
"amount": 5000,
"mean": "CBWeb",
"clubId": "/{clientToken}/clubs/1",
"clubCode": "AA1",
"checkout": "/{clientToken}/checkouts/1"
}
{
"contactId": "/{clientToken}/contacts/1",
"contactFamilyName": "Doe",
"contactGivenName": "John",
"contactNumber": "1",
"amount": 5000,
"mean": "card",
"clubId": "/{clientToken}/clubs/1",
"clubCode": "AA1",
"checkout": "/{clientToken}/checkouts/1",
"paymentCards": [
{
"amount": 5000,
"network": "CBGroup",
}
]
}
{
"contactId": "/{clientToken}/contacts/1",
"contactFamilyName": "Doe",
"contactGivenName": "John",
"contactNumber": "1",
"amount": 5000,
"mean": "cheque",
"clubId": "/{clientToken}/clubs/1",
"clubCode": "AA1",
"checkout": "/{clientToken}/checkouts/1",
"cheques": [ // A remplir uniquement pour les paiements en chèques
{
"amount": 5000,
"bank": "Crédit du nord",
"code": "1234567890",
"depositDesiredDate": "2019-04-15",
"owner": "John Doe"
}
]
}
Validate the sale by deferring the payment
It is allowed to validate a sale by deferring the payment. The contact will owe the amounts of the sale before the indicated date.
parameter | description | example |
---|---|---|
date | Date from which the payment will be considered "overdue" | 2019-02-28 |
POST `/{clientToken}/sales/{saleId}/postpone_due`
{
"date": "2019-02-28"
}
Validate the sale without payment / Complimentary products
If — and only if — you create a sale with a zero amount (0€, free product),
You can then complete the sale by applying a validation transition.
`POST /{client_token}/sales/{id}/transitions`
{
"transition": "validate"
}
The sale will then be recorded, and the (potential) product behaviors will be executed.