Skip to content

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:

  1. Identify the contact
  2. Retrieve the catalog corresponding to the contact
  3. Sell one or more products to the contact
  4. Verify the validity of the sale
  5. 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.

bash
GET `/{clientToken}/contacts/{contactId}`
bash
GET `/demoapi/contacts/1234`
json
{  
   "@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).

bash
GET `/{clientToken}/stats/accounts/contacts/:contactId`
bash
GET `/demoapi/stats/accounts/contacts/%2Fstadline%2Fcontacts%2F1`
json5
{
  // ...
  "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 ​

parameterdescriptionexample
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
typeProduct category. It is possible to filter by category. Allowed values: subscription, subscription_option, badge, counter, package, countermark, test_session, regularsubscription see category mappings below
pagePage number. The response contains 30 results per page, so pagination is supported.1
bash
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" products
  • subscription_option: "subscription option" products
  • badge: "badge" products
  • counter: "session booklets" products
  • package: packaged formula products
  • countermark: "countermark" products
  • test_session: "trial session" products
  • regular: "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:

parameterdescriptionexample
contactIdIdentifier of the contact targeted by the sale. This is an IRI, not just a numeric ID/{clientToken}/contacts/12345
contactFamilyNameContact's family name.Doe
contactGivenNameContact's given name.John
contactNumberContact's number.423456
clubIdClub identifier in the form of an IRI/{clientToken}/clubs/1
clubCodeClub codeABCDE
address[addressCountry]CountryFrance
address[addressLocality]CityLa Madeleine
address[postalCode]Postal code59110
address[streetAddress]Address41 rue du Général de Gaulle
bash
POST `/{clientToken}/sales`
bash
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"
    }
}'
json
{  
   "@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 ​

parameterdescriptionexample
offerIdIdentifier 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

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} ​

parameterdescriptionexample
saleIdCart identifier456

Example request ​

GET /{clientToken}/sales/1

json5
{
  // ...
  "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 payment
    • cash, for a cash payment
    • cheque, for a cheque payment
    • card, for an on-site card payment

POST /{clientToken}/sales/{saleId}/payments ​

parameterdescriptionexample
contactIdIdentifier of the contact making the payment/{clientToken}/contacts/456
contactFamilyNameLast name of the contact making the paymentDoe
contactGivenNameFirst name of the contact making the paymentJohn
contactNumberContact number of the contact making the payment98765
amountPayment amount in cents4995
meanPayment methodCBWeb
clubIdClub identifier as an IRI/{clientToken}/clubs/1
clubCodeClub codeABCDE
checkoutClub register identifier as an IRI/{clientToken}/checkouts/2
paymentCardsDetails 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
chequesDetails 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

mean=card payments and cash closing

When registering an on-site card payment (mean=card), the paymentCards array must be provided for the payment to appear in the cash closing (checkout closing / safe deposit).

A card payment sent without paymentCards is accepted by the API, but it will not appear in the cash closing.

  • Each paymentCards[][amount] is expressed in cents, and the sum of the paymentCards amounts must equal the payment amount (otherwise the request is rejected).
  • Only the CBGroup and AmEx networks are taken into account in the cash closing.

Example of a Web payment ​

bash
curl '/{clientToken}/sales/1/payments'
json5
{
    "contactId": "/{clientToken}/contacts/1",
    "contactFamilyName": "Doe",
    "contactGivenName": "John",
    "contactNumber": "1",
    "amount": 5000,
    "mean": "CBWeb",
    "clubId": "/{clientToken}/clubs/1",
    "clubCode": "AA1",
    "checkout": "/{clientToken}/checkouts/1"
}
json5
{
    "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",
        }
    ]
}
json5
{
    "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.

parameterdescriptionexample
dateDate from which the payment will be considered "overdue"2019-02-28
bash
POST `/{clientToken}/sales/{saleId}/postpone_due`
json
{
    "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.

bash
`POST /{client_token}/sales/{id}/transitions`
json
{
    "transition": "validate"
}

The sale will then be recorded, and the (potential) product behaviors will be executed.