External payment system integration

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.

  1. The user pays in your payment system.

  2. Your application receives the confirmation from that system.

  3. Your application tops up the user's wallet through the API and puts the payment reference into the transaction description.

  4. 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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 yourcompany.eu.ysoft.cloud. The API identifies your account by it.

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 client_id. Administrators see it as the Endpoint ID in Security › Endpoints.

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.

  1. Register the application. You do this once. A client_description helps administrators recognize it in Security › Endpoints.

    Bash
    curl -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, and interval, the number of seconds to wait between token requests.

  2. Ask an administrator to authorize schoolpay-connector in Security › Endpoints (select it, then Authorize selected).

  3. Get the device token. Until the application is authorized, the response is authorization_pending.

    Bash
    curl -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_token and its refresh_token.

  4. Sign in as the technical user with the device token.

    Bash
    curl -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_token and its refresh token in token.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:

    Bash
    curl -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):

    Bash
    curl -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.

Bash
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 add for top-ups and remove for refunds and corrections, both with "balanceType":"wallet". They are recorded as Top-up and Withdraw. A remove larger than the wallet balance fails with error 400; the balance never goes below zero.

  • Do not use set for 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 with datefrom and dateto.

  • 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 authorization_pending from the token request

An administrator has not authorized the application in Security › Endpoints yet. Keep polling.

403 No device authorization token provided from sign-in

Sign in with Authorization: Bearer <device token>. The API key does not allow untrusted endpoints.

401 Unauthorized from sign-in

Check the technical user's name and password.

403 No user authorization token provided

Sign in first and send Authorization: Bearer <user token>.

403 Invalid user token.

The token has expired or was issued for another API key. Sign in again.

403 Restricted API key is not allowed for this operation

Turn on Advanced API permissions on the API key.

403 Missing permissions in API key. Required: …

Add the listed permission to the API key's Advanced API permissions.

403 Access to the requested account or provider was denied. Required permissions: …

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 User with username=… and providerId=… not found.

Check the user name and the provider ID.

400 Removal failed: insufficient wallet balance for user …

The wallet holds fewer credits than you tried to remove.

400 Invalid operation … or Invalid balance type …

Use add or remove, and wallet.