In-App Purchase (IAP) allows providers of ongoing services through Odoo apps to be compensated for ongoing service use rather than — and possibly instead of — a sole initial purchase.
In that context, Odoo acts mostly as a broker between a client and an Odoo App Developer:
Users purchase service tokens from Odoo.
Service providers draw tokens from the user’s Odoo account when service is requested.
The Service Provider is (probably) you the reader, you will be providing value to the client in the form of a service paid per-use.
The Client installed your Odoo App, and from there will request services.
Odoo brokers crediting, the Client adds credit to their account, and you can draw credits from there to provide services.
The External Service is an optional player: you can either provide a service directly, or you can delegate the actual service acting as a bridge/translator between an Odoo system and the actual service.
If everything goes well, the normal flow is the following:
The Client requests a service of some sort.
The Service Provider asks Odoo if there are enough credits for the service in the Client’s account, and creates a transaction over that amount.
The Service Provider provides the service (either on their own or calling to External Services).
The Service Provider goes back to Odoo to capture (if the service could be provided) or cancel (if the service could not be provided) the transaction created at step 2.
Finally, the Service Provider notifies the Client that the service has been rendered, possibly (depending on the service) displaying or storing its results in the client’s system.
However, if the Client’s account lacks credits for the service, the flow will be as follows:
The Client requests a service as previously.
The Service Provider asks Odoo if there are enough credits on the Client’s account and gets a negative reply.
This is signaled back to the Client.
Who is redirected to their Odoo account to credit it and re-try.
Building your service
For this example, the service we will provide is ~~mining dogecoins~~ burning 10 seconds of CPU for a credit. For your own services, you could, for example:
provide an online service yourself (e.g. convert quotations to faxes for business in Japan);
provide an offline service yourself (e.g. provide accountancy service); or
act as intermediary to an other service provider (e.g. bridge to an MMS gateway).
Register the service on Odoo
The first step is to register your service on the IAP endpoint (production and/or test) before you can actually query user accounts. To create a service, go to your Portal Account on the IAP endpoint (https://iap.odoo.com for production, https://iap-sandbox.odoo.com for testing, the endpoints are independent and not synchronized). Alternatively, you can go to your portal on Odoo (https://iap.odoo.com/my/home) and select In-App Services.
Log in then go to, click Create and provide the information of your service.
The service has seven important fields:
ServiceName: This is the string you will need to provide inside the client’s app when requesting a transaction from Odoo. (e.g.
self.env['iap.account].get(name)). As good practice, this should match the technical name of your app.
Label: The name displayed on the shopping portal for the client.
Icon: A generic icon that will serve as default for your packs.
ServiceKey: The developer key that identifies you in IAP (see your service) and allows to draw credits from the client’s account. It will be shown only once upon creation of the service and can be regenerated at will.
Float: This corresponds to the credits you are ready to offer upon first use to your app users. Note that such service will only be available to clients that have an active enterprise contract.
You can then create credit packs which clients can purchase in order to use your service.
A credit pack is essentially a product with five characteristics:
Name: name of the pack,
Icon: specific icon for the pack (if not provided, it will fallback on the service icon),
Description: details on the pack that will appear on the shop page as well as the invoice,
Amount: amount of credits the client is entitled to when buying the pack,
Price: price in EUR (for the time being, USD support is planned).
The second step is to develop an Odoo App which clients can install in their Odoo instance and through which they can request the services you provide. Our app will just add a button to the Partners form which lets a user request burning some CPU time on the server.
First, we will create an odoo module depending on
iap. IAP is a standard V11 module and the dependency ensures a local account is properly set up and we will have access to some necessary views and useful helpers.
Second, the “local” side of the integration. Here we will only be adding an action button to the partners view, but you can of course provide significant local value via your application and additional parts via a remote service.
We can now implement the action method/callback. This will call our own server.
There are no requirements when it comes to the server or the communication protocol between the app and our server, but
iap provides a
iap_jsonrpc() helper to call a JSON-RPC2 endpoint on an other Odoo instance and transparently re-raise relevant Odoo exceptions (
In that call, we will need to provide:
any relevant client parameter (none here),
tokenof the current client that is provided by the
account_tokenfield. You can retrieve the account for your service by calling
service_nameis the name of the service registered on IAP endpoint.
Though that is not required, since
iap provides both a client helper for JSON-RPC2 calls (
iap_jsonrpc()) and a service helper for transactions (
iap_charge) we will also be implementing the service side as an Odoo module:
iap_charge helper will:
authorize (create) a transaction with the specified number of credits, if the account does not have enough credits it will raise the relevant error
execute the body of the
if the body of the
withexecutes successfully, update the price of the transaction if needed
capture (confirm) the transaction
otherwise, if an error is raised from the body of the
with, cancel the transaction (and release the hold on the credits)
iap_charge helper has two additional optional parameters we can use to make things clearer to the end-user.
is a message which will be associated with the transaction and will be displayed in the user’s dashboard, it is useful to remind the user why the charge exists.
is the name of a QWeb Templates template which will be rendered and shown to the user if their account has less credit available than the service provider is requesting, its purpose is to tell your users why they should be interested in your IAP offers.
JSON-RPC2 Transaction API
The IAP transaction API does not require using Odoo when implementing your server gateway, calls are standard JSON-RPC2.
Calls use different endpoints but the same method on all endpoints (
Exceptions are returned as JSON-RPC2 errors, the formal exception name is available on
data.namefor programmatic manipulation.
Confirms the specified transaction, transferring the reserved credits from the user’s account to the service provider’s.
Capture calls are idempotent: performing capture calls on an already captured transaction has no further effect.
Cancels the specified transaction, releasing the hold on the user’s credits.
Cancel calls are idempotent: performing capture calls on an already cancelled transaction has no further effect.
Exceptions aside, these are abstract types used for clarity, you should not care how they are implemented.
String identifying your service on https://iap.odoo.com (production) as well as the account related to your service in the client’s database.
Identifier generated for the provider’s service. Each key (and service) matches a token of a fixed value, as generated by the service provide.
Multiple types of tokens correspond to multiple services. As an exampe, SMS and MMS could either be the same service (with an MMS being ‘worth’ multiple SMS) or could be separate services at separate price points.
Identifier for a user account.
Transaction identifier, returned by the authorization process and consumed by either capturing or cancelling the transaction.
Raised during transaction authorization if the credits requested are not currently available on the account (either not enough credits or too many pending transactions/existing holds).
any operation to which a service token is required, if the service token is invalid; or
any failure in an inter-server call. (typically, in
Raised by any unexpected behaviour at the discretion of the App developer (you).
Test the API
In order to test the developed app, we propose a sandbox platform that allows you to:
Test the whole flow from the client’s point of view - Actual services and transactions that can be consulted. (again this requires to change the endpoint, see the danger note in Service).
Test the API.
The latter consists in specific tokens that will work on IAP-Sandbox only.
000000: Represents a non-existing account. Returns an
InsufficientCreditErroron authorize attempt.
000111: Represents an account without sufficient credits to perform any service. Returns an
InsufficientCreditErroron authorize attempt.
111111: Represents an account with enough credits to perform any service. An authorize attempt will return a dummy transaction token that is processed by the capture and cancel routes.
For convenience, if you are implementing your service using Odoo the
iap module provides a few helpers to make IAP flow even simpler.
iap_charge(env, key, account_token, credit[, dbuuid, description, credit_template, ttl])
A context manager for authorizing and automatically capturing or cancelling transactions for use in the backend/proxy.
Works much like e.g. a cursor context manager:
immediately authorizes a transaction with the specified parameters;
if the body executes in full without error, captures the transaction;
otherwise cancels it.
Will authorize everything.
iap_cancel(env, transaction_token, key)
Will cancel an authorized transaction.
iap_capture(env, transaction_token, key, credit)
Will capture the amount
crediton the given transaction.