External payment system integration
Your own application can top up and deduct users' credits through the PMC API. Use it when users pay through a system other than the built-in PayPal payment gateway, for example a school payment portal or a cash desk application, and you do not want administrators to top up by hand on the Balance tab.
The application can top up a wallet, deduct credits, and read balances and transactions. All endpoints and their parameters are described in API Functions, section Balance.
How it works
PMC never sees the payment. It only records the credits your application sends.
-
The user pays in your payment system.
-
Your application receives the confirmation from that system.
-
Your application tops up the user's wallet through the API and puts the payment reference into the transaction description.
-
The credits are available at once. The transaction appears on the Balance tab and in the user's My Balance.
Before you start
In the Cloud Portal Web UI:
-
Turn on balance in Balance › Settings, see Setting up Balance. The API works even while balance is off, so turning it off does not stop the application.
-
Make sure the users you top up have a policy with a wallet (Wallet or Quota + Wallet), see Balance policies. The API does not check the policy: credits sent to a user whose policy has no wallet are added to the wallet, but the user cannot spend them until the policy includes a wallet.
-
Create a dedicated technical user for the application and a role with ModifyUserBalance (top-ups and refunds) and ViewUserBalance (reading balances and history), see Roles. Assign the role to the user in Users › Access control, see Access Control. Permission changes apply at once, so removing the role stops the application immediately. The application signs in as this user, so every top-up and refund it makes is recorded under the user's name, shown as "by <user name>" on the Balance tab and in My Balance.
-
Create an API key for the application, see API access keys. Turn on Advanced API permissions and select only ModifyUserBalance and ViewUserBalance. Leave Allow unauthenticated requests, Allow untrusted endpoints and Automatic endpoint trust off. If the application runs on a fixed address, enter it in Allowed IPs.
Your application then needs these values:
|
Value |
Where to find it |
|---|---|
|
Server address |
The domain of your account, for example |
|
API key |
Security › API access keys. The value is shown only once, when the key is created. |
|
Provider ID |
The ID of the authentication provider the users belong to. Open the provider in Users › Authentication providers; the ID is shown under Type. |
|
Application name |
A unique name you choose, sent as |
|
Technical user |
User name and password of the user from step 3. |
Choosing how your application signs in
The Balance endpoints accept only a user token, so the application signs in as the technical user. We recommend the device code flow, because only a registered, trusted application can then sign in.
|
Method |
How it works |
Use it |
|---|---|---|
|
Device code flow |
The application registers once as a trusted device and gets a device token. With the device token it signs in as the technical user and gets a user token for the Balance calls. |
Recommended |
|
User sign-in |
The application signs in as the technical user with user name and password, without a device token. The API key must allow untrusted endpoints. |
Only when the application cannot register as a trusted device |
|
API key only |
No sign-in. The API key allows unauthenticated requests and untrusted endpoints, so anyone who has the key can change balances. |
Not recommended |
The registration, token and refresh calls are described in Register device, Trusted device registration and User and device tokens; authorizing endpoints in Endpoints.
Signing in the application
The application registers and signs in once, and afterwards only keeps its tokens valid. Each top-up is then a single call, see the next section. The examples use the device code flow and the application name schoolpay-connector.
-
Register the application. You do this once. A
client_descriptionhelps administrators recognize it in Security › Endpoints.Bashcurl -X POST "https://<server>:7400/oauth/v1/register" \ -d "client_id=schoolpay-connector" -d "client_secret=<API key>" \ -d "client_type=server" -d "response_type=device_code" \ --data-urlencode "client_description=SchoolPay payment connector"The response contains
device_code, valid for 72 hours, andinterval, the number of seconds to wait between token requests. -
Ask an administrator to authorize
schoolpay-connectorin Security › Endpoints (select it, then Authorize selected). -
Get the device token. Until the application is authorized, the response is
authorization_pending.Bashcurl -X POST "https://<server>:7400/oauth/v1/token" \ -d "client_id=schoolpay-connector" -d "client_secret=<API key>" \ -d "grant_type=device_code" -d "device_code=<device_code>"The response contains the device token in
access_tokenand itsrefresh_token. -
Sign in as the technical user with the device token.
Bashcurl -X POST "https://<server>:7300/api/v1/login" \ -H "X-Api-Key: <API key>" -H "Authorization: Bearer <device token>" \ --data-urlencode "authtype=0" --data-urlencode "userid=<technical user>" --data-urlencode "password=<password>"The response contains the user token in
token.access_tokenand its refresh token intoken.refresh_token.
Keeping the tokens valid
By default the device token and the user token are valid for 1 hour and the refresh tokens for 90 days. Every refresh returns a new refresh token. Store it at once and use only the latest one: presenting an older refresh token after a newer one has been used revokes all the application's tokens. If several instances of your application run at the same time, let only one of them refresh the tokens. Keep the API key, the technical user's password and all tokens in secure storage, never in source code or logs.
-
Refresh the device token:
Bashcurl -X POST "https://<server>:7400/oauth/v1/token" \ -d "client_id=schoolpay-connector" -d "client_secret=<API key>" \ -d "grant_type=refresh_token" -d "refresh_token=<device refresh token>" -
Refresh the user token, or sign in again with a valid device token (step 4):
Bashcurl -H "X-Api-Key: <API key>" "https://<server>:7300/api/v1/token?refresh_token=<user refresh token>" -
If the device refresh token expires or is revoked, register the application again (steps 1 to 3). An administrator must first delete the old entry in Security › Endpoints, otherwise the registration fails with
duplicate_client_id.
Topping up a wallet
With a valid user token, a top-up is a single call. The user jsmith from provider 12345 paid for 10 credits. The amount is always in credits, so convert the payment with your own price per credit. Put your payment reference into description.
curl -X POST "https://<server>:7300/api/v1/balance/transactions" \
-H "X-Api-Key: <API key>" -H "Authorization: Bearer <user token>" -H "Content-Type: application/json" \
--data '{"userName":"jsmith", "providerId":12345, "operation":"add", "balanceType":"wallet", "amount":10, "description":"SchoolPay payment 2026-000123"}'
The response is the new transaction (transactionType TOP_UP) with the balances after the top-up in quotaBalanceAfter and walletBalanceAfter. If the response is 403 Invalid user token., nothing was credited: refresh the user token or sign in again, then send the request again.
To read a balance or the transaction history, use GET /balance and GET /balance/transactions, see API Functions.
Refunds and repeated requests
-
Use
addfor top-ups andremovefor refunds and corrections, both with"balanceType":"wallet". They are recorded as Top-up and Withdraw. Aremovelarger than the wallet balance fails with error 400; the balance never goes below zero. -
Do not use
setfor payments. It works only on the quota and overwrites it; it is meant for resets. -
Repeated requests are not detected. The same top-up sent twice is credited twice. Keep your own record of credited payments. Before you retry a request whose response did not arrive, look for its payment reference in
GET /balance/transactions. The history cannot be filtered by description, so narrow it withdatefromanddateto. -
Administrators see these transactions on the user's balance in Balance, with the technical user as the author. The filter above Recent transactions searches descriptions, so they can find a payment by its reference, see Managing user balances.
-
Users see the same transactions in My Balance, including the description and the technical user's name. Use a payment reference the user recognizes, keep personal and card data out of the description, and give the technical user a name users understand.
Troubleshooting
|
Response |
What to check |
|---|---|
|
400 |
An administrator has not authorized the application in Security › Endpoints yet. Keep polling. |
|
403 |
Sign in with |
|
401 |
Check the technical user's name and password. |
|
403 |
Sign in first and send |
|
403 |
The token has expired or was issued for another API key. Sign in again. |
|
403 |
Turn on Advanced API permissions on the API key. |
|
403 |
Add the listed permission to the API key's Advanced API permissions. |
|
403 |
The same message covers several causes: the technical user does not have the listed permission on this account, or the provider ID or the API key belongs to another account. |
|
404 |
Check the user name and the provider ID. |
|
400 |
The wallet holds fewer credits than you tried to remove. |
|
400 |
Use |