Premium SMS
Premium SMS is a micropayment method where the customer sends a text message to a special number and the cost is added to their phone bill. dpay.pl handles verification of the return codes.
How it works
/api/v1/sms/tariffsFull list of available Premium SMS tariffs.Full contract in the API Reference
GET/api/v1/sms/verify/{client}/{service}/{code}SMS code verification contract: parameters and response codes.Full contract in the API Reference
Available endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/v1/sms/tariffs | GET | List of all available SMS tariffs |
/api/v1/sms/verify/{client}/{service}/{code} | GET | SMS code verification |
Step 1: Retrieve the tariff list
Fetch the available SMS tariffs so you can show the customer the right number and message text:
GET https://panel.dpay.pl/api/v1/sms/tariffs
Example response
The response is a flat array of objects - it is not wrapped in any key:
[
{
"id": 1,
"number": "7043",
"vat": "0.65",
"netto": "0.50",
"public": 1,
"18plus": 0
},
{
"id": 3,
"number": "72240",
"vat": "2.46",
"netto": "2.00",
"public": 1,
"18plus": 0
},
{
"id": 24,
"number": "91986",
"vat": "23.37",
"netto": "19.00",
"public": 1,
"18plus": 1
}
]
Tariff fields
| Field | Description |
|---|---|
id | Tariff identifier. Code verification returns it in the tariff field. |
number | SMS number the customer sends the message to |
netto | Net amount in PLN |
vat | Gross amount in PLN - the cost paid by the customer |
public | 1 - tariff is publicly available |
18plus | 1 - tariff requires age confirmation |
The endpoint returns all rows of the tariff table, including adult-content tariffs (18plus: 1) and any tariffs withdrawn from the public offer (public: 0). Filter them out on your side before showing the list to a customer.
vat is an amount, not a rateDespite its name, vat does not hold a percentage rate (23%) - it holds the gross amount in PLN. Compute the VAT amount as vat - netto.
Step 2: Show the instructions to the customer
Based on the selected tariff, show the customer how to send the message:
<div class="sms-instruction">
<p>To pay, send a text message with the following content:</p>
<p class="sms-text"><strong>DPAY.ABC123</strong></p>
<p>to the number:</p>
<p class="sms-number"><strong>72240</strong></p>
<p>Cost: 2.46 PLN gross</p>
</div>
<form id="sms-verify-form">
<label for="sms-code">Enter the code from the return SMS:</label>
<input type="text" id="sms-code" name="code" placeholder="abcdefgh" required />
<button type="submit">Verify code</button>
</form>
The SMS content is the tariff prefix, a dot and your service name: DPAY.{service}. The exact content is configured in the dpay.pl panel.
Step 3: Verify the SMS code
Once you have the code from the customer, verify it through the API:
GET https://panel.dpay.pl/api/v1/sms/verify/{client}/{service}/{code}
A successful verification marks the code as used and records the IP and usage date. Calling it again for the same code returns err3. Treat this call as a write operation rather than a status read - call it exactly once and persist the result on your side.
URL parameters
| Parameter | Description | Example |
|---|---|---|
client | Numeric client identifier from the panel | 1042 |
service | Numeric identifier of the registered SMS service | 77 |
code | Code from the return SMS - eight lowercase letters | abcdefgh |
Example request
curl -X GET "https://panel.dpay.pl/api/v1/sms/verify/1042/77/abcdefgh"
Response - valid code
HTTP 200:
{
"status": true,
"msisdn": "48601234567",
"code": "abcdefgh",
"tariff": 3,
"number": "72240",
"vat": "2.46",
"net": "2.00",
"net_gross": "1.20",
"revenue": "60.00"
}
| Field | Description |
|---|---|
status | true - the code was valid and has just been consumed |
msisdn | Phone number the SMS was sent from |
code | The verified code |
tariff | Tariff id (matches id from the tariff list) |
number | SMS number of the tariff |
vat | Gross amount of the tariff - the customer's cost |
net | Net amount of the tariff |
net_gross | Your revenue: net × revenue / 100 |
revenue | Your percentage share of the net amount, as it stood when the SMS was sent |
Responses - errors
Error responses share a common shape with an errorcode field. Branch on status, not on error and not on the HTTP status code - error is false for two of the three errors:
errorcode | HTTP | error | message | Cause |
|---|---|---|---|---|
err1 | 400 | true | Could not find required parameters or they are wrong | Malformed parameters, or no SMS history at all for the client + service pair |
err2 | 200 | false | Code does not exists | The code does not exist for this client + service pair |
err3 | 200 | false | Code used | The code has already been consumed |
{
"status": false,
"error": false,
"errorcode": "err3",
"message": "Code used"
}
Full example - PHP
<?php
$clientId = getenv('DPAY_SMS_CLIENT_ID'); // numeric client ID
$service = getenv('DPAY_SMS_SERVICE_ID'); // numeric SMS service ID
$code = $_POST['code'] ?? '';
if (!preg_match('/^[a-z]{8}$/', $code)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid code format']);
exit;
}
$url = sprintf(
'https://panel.dpay.pl/api/v1/sms/verify/%s/%s/%s',
urlencode($clientId),
urlencode($service),
urlencode($code)
);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
]);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
// The status field is the only reliable indicator - the HTTP code is 200
// for the err2 and err3 errors as well.
if (($result['status'] ?? false) === true) {
activateService($_POST['user_id'], $result['net_gross']);
echo json_encode(['success' => true, 'msg' => 'Payment verified']);
} elseif (($result['errorcode'] ?? '') === 'err3') {
echo json_encode(['error' => true, 'msg' => 'Code already used']);
} else {
echo json_encode(['error' => true, 'msg' => 'Invalid SMS code']);
}
Full example - Node.js
const axios = require('axios');
app.post('/api/verify-sms', async (req, res) => {
const { code } = req.body;
const clientId = process.env.DPAY_SMS_CLIENT_ID; // numeric client ID
const service = process.env.DPAY_SMS_SERVICE_ID; // numeric SMS service ID
if (!/^[a-z]{8}$/.test(code)) {
return res.status(400).json({ error: 'Invalid code format' });
}
try {
// validateStatus - err1 comes back with HTTP 400, so we do not want a throw
const response = await axios.get(
`https://panel.dpay.pl/api/v1/sms/verify/${clientId}/${service}/${code}`,
{ validateStatus: (s) => s === 200 || s === 400 }
);
const result = response.data;
if (result.status === true) {
await activateService(req.user.id, result.net_gross);
res.json({ success: true, msg: 'Payment verified' });
} else if (result.errorcode === 'err3') {
res.json({ error: true, msg: 'Code already used' });
} else {
res.json({ error: true, msg: 'Invalid SMS code' });
}
} catch (error) {
res.status(500).json({ error: true, msg: 'Verification failed' });
}
});
Best practices
1. One-time codes
Every SMS code can be used only once, and the verification call itself consumes it. Do not call this endpoint in a loop or on page refresh - persist the result of the first call and make your decisions from that.
2. Validate the code format
Validate the code format before calling the API:
if (!preg_match('/^[a-z]{8}$/', $code)) {
// The code does not match the required format
}
3. Store the verifications
Record verified codes in your database so you can resolve any future complaints:
$stmt = $pdo->prepare('INSERT INTO sms_payments (code, net_gross, number, msisdn, user_id, verified_at) VALUES (?, ?, ?, ?, ?, NOW())');
$stmt->execute([$code, $result['net_gross'], $result['number'], $result['msisdn'], $userId]);
Common errors
errorcode | Cause | Resolution |
|---|---|---|
err1 | Malformed parameters, or no SMS history for the client + service pair | Check that client and service are the numeric identifiers from the panel, not names |
err2 | The code does not exist | Ask the customer to re-check the code from the return SMS |
err3 | The code has already been used | Explain that the code is single-use |
Premium SMS works best as a micropayment method for digital content, premium access or virtual items in games.