From 0f80de1f05f9e928872b419cd85ea42bd6161222 Mon Sep 17 00:00:00 2001 From: Alireza Hassani Date: Sat, 10 Oct 2026 14:10:54 +0330 Subject: [PATCH] Send verification SMS on the service line so carriers deliver the code. Co-authored-by: Cursor --- .env.example | 2 ++ scripts/send-sms.ts | 6 +++++- src/auth/auth.service.ts | 1 + src/sms/sms.service.ts | 9 +++++++++ src/sms/sms.types.ts | 2 ++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index a353fc5..4032780 100644 --- a/.env.example +++ b/.env.example @@ -35,5 +35,7 @@ MEDIA_MAX_FILE_SIZE_MB=10 MESHKEE_SMS_URL=https://api.meshkee.com/api/v1/public/sms/send MESHKEE_SMS_API_KEY= MESHKEE_SMS_DOMAIN=manacollections.ir +# Service line. Omit and the gateway uses the advertising shortcode, which does not deliver OTP. +MESHKEE_SMS_SOURCE=5000110005 # Comma-separated admin mobiles for alerts SMS_NOTIFY_MOBILES= diff --git a/scripts/send-sms.ts b/scripts/send-sms.ts index 462cc6d..5036547 100644 --- a/scripts/send-sms.ts +++ b/scripts/send-sms.ts @@ -24,6 +24,10 @@ async function main() { 'https://api.meshkee.com/api/v1/public/sms/send'; const apiKey = process.env.MESHKEE_SMS_API_KEY; const domain = process.env.MESHKEE_SMS_DOMAIN ?? 'manacollections.ir'; + const source = + process.argv.includes('--source') + ? arg('source') + : (process.env.MESHKEE_SMS_SOURCE ?? '5000110005'); if (!apiKey) throw new Error('MESHKEE_SMS_API_KEY is not set in .env'); if (!/^09\d{9}$/.test(to)) throw new Error('to must match 09xxxxxxxxx'); @@ -35,7 +39,7 @@ async function main() { 'Content-Type': 'application/json', 'X-Api-Key': apiKey, }, - body: JSON.stringify({ domain, to, message }), + body: JSON.stringify({ domain, to, message, source }), }); const body = await response.json().catch(() => null); diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 4259fe6..21bf61c 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -379,6 +379,7 @@ export class AuthService { await this.sms.send({ to: input.cellNumber, message: input.message(code), + source: this.sms.otpSource(), }); return { diff --git a/src/sms/sms.service.ts b/src/sms/sms.service.ts index fd7d008..8ae67aa 100644 --- a/src/sms/sms.service.ts +++ b/src/sms/sms.service.ts @@ -20,6 +20,14 @@ export class SmsService { this.domain = this.config.getOrThrow('MESHKEE_SMS_DOMAIN'); } + /** + * Service shortcode (خط خدماتی). The partner gateway otherwise sends from + * the advertising line, which operators drop for verification texts. + */ + otpSource(): string { + return this.config.get('MESHKEE_SMS_SOURCE')?.trim() || '5000110005'; + } + notifyMobiles(): string[] { const raw = this.config.get('SMS_NOTIFY_MOBILES', ''); return raw @@ -69,6 +77,7 @@ export class SmsService { domain: this.domain, to, message, + ...(input.source ? { source: input.source } : {}), }), }); } catch (err) { diff --git a/src/sms/sms.types.ts b/src/sms/sms.types.ts index a3533ec..812c42a 100644 --- a/src/sms/sms.types.ts +++ b/src/sms/sms.types.ts @@ -1,6 +1,8 @@ export type SendSmsInput = { to: string; message: string; + /** Gama shortcode. OTP must use the service line; the partner default is advertising. */ + source?: string; }; export type SendSmsResult = {