Skip to content

Commit 3004ae9

Browse files
u0x01charIeszhao
andauthored
feat(connector): add YunPian SMS connector (#6906)
* feat(connector): add YunPian SMS connector * chore: update README and pnpm lock * chore: update SVG and error messages --------- Co-authored-by: Charles Zhao <charleszhao@silverhand.io>
1 parent 3fa2b79 commit 3004ae9

File tree

12 files changed

+603
-1
lines changed

12 files changed

+603
-1
lines changed

.changeset/brown-donkeys-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@logto/connector-yunpian-sms": minor
3+
---
4+
5+
add YunPian SMS connector

packages/connectors/connector-xiaomi/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
"build": "tsup",
2727
"dev": "tsup --watch",
2828
"lint": "eslint --ext .ts src",
29-
"lint:fix": "eslint --ext .ts src --fix",
3029
"lint:report": "pnpm lint --format json --output-file report.json",
3130
"test": "vitest src",
3231
"test:ci": "pnpm run test --silent --coverage",
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Yunpian SMS connector
2+
3+
The official Logto connector for Yunpian SMS service. [中文文档](https://github.com/logto-io/logto/tree/master/packages/connectors/connector-yunpian-sms/README.zh-CN.md)
4+
5+
**Table of contents**
6+
7+
- [Yunpian SMS connector](#yunpian-sms-connector)
8+
- [Get started](#get-started)
9+
- [Set up SMS service in Yunpian Console](#set-up-sms-service-in-yunpian-console)
10+
- [Create a Yunpian account](#create-a-yunpian-account)
11+
- [Get API KEY](#get-api-key)
12+
- [Configure SMS templates](#configure-sms-templates)
13+
- [Configure in Logto](#configure-in-logto)
14+
- [Notes](#notes)
15+
- [References](#references)
16+
17+
## Get started
18+
19+
Yunpian is a communication service provider offering various services including SMS. The Yunpian SMS Connector is a plugin provided by the Logto team to integrate with Yunpian's SMS service, enabling Logto end-users to register and sign in via SMS verification codes.
20+
21+
## Set up SMS service in Yunpian Console
22+
23+
### Create a Yunpian account
24+
25+
Visit [Yunpian's website](https://www.yunpian.com/) to register an account and complete real-name verification.
26+
27+
### Get API KEY
28+
29+
1. Log in to Yunpian Console
30+
2. Go to "Account Settings" -> "Sub-account Management"
31+
3. Find and copy the API KEY
32+
33+
### Configure SMS templates
34+
35+
1. In Yunpian Console, go to "Domestic SMS" -> "Signature Filing"
36+
2. Create and submit a signature, wait for carrier approval
37+
3. Go to "Domestic SMS" -> "Template Filing" and select "Verification Code"
38+
4. Create a verification code template, ensure it includes the `#code#` variable (you can also use "Common Templates" to speed up the approval process)
39+
5. Wait for template approval
40+
6. If you need to send international SMS, repeat the above steps but select "International SMS" -> "Template Filing"
41+
42+
## Configure in Logto
43+
44+
1. In Logto Console, go to "Connectors"
45+
2. Find and click "Yunpian SMS Service"
46+
3. Fill in the configuration form:
47+
- API KEY: The API KEY obtained from Yunpian
48+
- SMS templates: Configure templates according to usage, ensure they match exactly with approved templates in Yunpian
49+
50+
## Notes
51+
52+
1. SMS template content must match exactly with the approved template in Yunpian
53+
2. The verification code variable placeholder in Yunpian templates is `#code#`, while in the connector configuration it's `{{code}}`
54+
3. Yunpian automatically adds the default signature based on API KEY, no need to include signature in the template content
55+
4. It's recommended to test the configuration before formal use
56+
57+
## References
58+
59+
- [Yunpian Development Documentation](https://www.yunpian.com/official/document/sms/zh_CN/introduction_brief)
60+
- [Logto SMS Connector Guide](https://docs.logto.io/docs/recipes/configure-connectors/sms-connector/)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 云片网短信连接器
2+
3+
云片网短信服务 Logto 官方连接器
4+
5+
**目录**
6+
7+
- [云片网短信连接器](#云片网短信连接器)
8+
- [开始使用](#开始使用)
9+
- [在云片网中配置](#在云片网中配置)
10+
- [创建云片网账号](#创建云片网账号)
11+
- [获取 API KEY](#获取-api-key)
12+
- [配置短信模板](#配置短信模板)
13+
- [在 Logto 中配置](#在-logto-中配置)
14+
- [注意事项](#注意事项)
15+
- [参考](#参考)
16+
17+
## 开始使用
18+
19+
云片网是一家通信服务提供商,提供包括短信在内的多种通信服务。云片网 SMS 连接器是由 Logto 团队提供的插件,用于调用云片网的短信服务,帮助 Logto 终端用户通过短信验证码进行注册和登录。
20+
21+
## 在云片网中配置
22+
23+
### 创建云片网账号
24+
25+
访问[云片网官网](https://www.yunpian.com/),注册账号并完成实名认证。
26+
27+
### 获取 API KEY
28+
29+
1. 登录云片网控制台
30+
2. 进入"账户设置" -> "子账户管理"
31+
3. 找到并复制 API KEY
32+
33+
### 配置短信模板
34+
35+
1. 在云片网控制台中进入"国内短信" -> "签名报备"
36+
2. 创建签名并提交,等待运营商审核通过
37+
3. 在云片网控制台中进入"国内短信" -> "模板报备",选择"验证码类"
38+
4. 创建验证码类短信模板,确保模板中包含 `#code#` 变量(也可以直接使用`常用模板`申请,加快审核速度)
39+
5. 等待模板审核通过
40+
6. 如果您需要发送国际短信,请重复上述步骤,选择"国际短信" -> "模板报备"并提交
41+
42+
## 在 Logto 中配置
43+
44+
1. 在 Logto 管理控制台中转到"连接器"
45+
2. 找到并点击"云片短信服务"
46+
3. 在配置表单中填入:
47+
- API KEY: 从云片网获取的 API KEY
48+
- 短信模板: 按照用途配置相应的模板内容,确保与云片网审核通过的模板内容一致
49+
50+
## 注意事项
51+
52+
1. 短信模板内容必须与云片网审核通过的模板完全一致
53+
2. 短信审核模板中的验证码变量占位符为 `#code#`,模板配置中验证码变量占位符为 `{{code}}`
54+
3. 云片网会自动根据 API KEY 添加默认签名,无需在发送模板内容中包含签名
55+
4. 建议在正式使用前进行测试,确保配置正确
56+
57+
## 参考
58+
59+
- [云片网开发文档](https://www.yunpian.com/official/document/sms/zh_CN/introduction_brief)
60+
- [Logto SMS 连接器指南](https://docs.logto.io/zh-CN/connectors/sms-connectors)
Lines changed: 13 additions & 0 deletions
Loading
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"name": "@logto/connector-yunpian-sms",
3+
"version": "1.0.0",
4+
"description": "云片网 SMS connector implementation.",
5+
"author": "Silverhand Inc. <contact@silverhand.io>",
6+
"dependencies": {
7+
"@logto/connector-kit": "workspace:^4.0.0",
8+
"@silverhand/essentials": "^2.9.1",
9+
"got": "^14.0.0",
10+
"zod": "^3.23.8"
11+
},
12+
"main": "./lib/index.js",
13+
"module": "./lib/index.js",
14+
"exports": "./lib/index.js",
15+
"license": "MPL-2.0",
16+
"type": "module",
17+
"files": [
18+
"lib",
19+
"docs",
20+
"logo.svg",
21+
"logo-dark.svg"
22+
],
23+
"scripts": {
24+
"precommit": "lint-staged",
25+
"check": "tsc --noEmit",
26+
"build": "tsup",
27+
"dev": "tsup --watch",
28+
"lint": "eslint --ext .ts src",
29+
"lint:report": "pnpm lint --format json --output-file report.json",
30+
"test": "vitest src",
31+
"test:ci": "pnpm run test --silent --coverage",
32+
"prepublishOnly": "pnpm build"
33+
},
34+
"engines": {
35+
"node": "^20.9.0"
36+
},
37+
"eslintConfig": {
38+
"extends": "@silverhand",
39+
"settings": {
40+
"import/core-modules": [
41+
"@silverhand/essentials",
42+
"got",
43+
"nock",
44+
"snakecase-keys",
45+
"zod"
46+
]
47+
}
48+
},
49+
"prettier": "@silverhand/eslint-config/.prettierrc",
50+
"publishConfig": {
51+
"access": "public"
52+
},
53+
"devDependencies": {
54+
"@silverhand/eslint-config": "6.0.1",
55+
"@silverhand/ts-config": "6.0.0",
56+
"@types/node": "^20.11.20",
57+
"@types/supertest": "^6.0.2",
58+
"@vitest/coverage-v8": "^2.1.8",
59+
"eslint": "^8.56.0",
60+
"lint-staged": "^15.0.2",
61+
"nock": "^13.3.1",
62+
"prettier": "^3.0.0",
63+
"supertest": "^7.0.0",
64+
"tsup": "^8.3.0",
65+
"typescript": "^5.5.3",
66+
"vitest": "^2.1.8"
67+
}
68+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import type { ConnectorMetadata } from '@logto/connector-kit';
2+
import { ConnectorConfigFormItemType } from '@logto/connector-kit';
3+
4+
export const endpoint = 'https://sms.yunpian.com/v2/sms/single_send.json';
5+
6+
export const defaultMetadata: ConnectorMetadata = {
7+
id: 'yunpian-sms',
8+
target: 'yunpian-sms',
9+
platform: null,
10+
name: {
11+
en: 'YunPian SMS Service',
12+
zh: '云片短信服务',
13+
},
14+
logo: './logo.svg',
15+
logoDark: null,
16+
description: {
17+
en: 'YunPian is a SMS service provider.',
18+
zh: '云片网是一家短信服务提供商。',
19+
},
20+
readme: './README.md',
21+
formItems: [
22+
{
23+
key: 'apikey',
24+
label: 'API Key',
25+
type: ConnectorConfigFormItemType.Text,
26+
required: true,
27+
placeholder: '<api-key>',
28+
},
29+
{
30+
key: 'templates',
31+
label: 'SMS Template',
32+
type: ConnectorConfigFormItemType.Json,
33+
required: true,
34+
defaultValue: [
35+
{
36+
usageType: 'SignIn',
37+
content: '您的验证码是 {{code}}。如非本人操作,请忽略本短信',
38+
},
39+
{
40+
usageType: 'Register',
41+
content: '您的验证码是 {{code}}。如非本人操作,请忽略本短信',
42+
},
43+
{
44+
usageType: 'ForgotPassword',
45+
content: '您的验证码是 {{code}}。如非本人操作,请忽略本短信',
46+
},
47+
{
48+
usageType: 'Generic',
49+
content: '您的验证码是 {{code}}。如非本人操作,请忽略本短信',
50+
},
51+
],
52+
},
53+
{
54+
key: 'enableInternational',
55+
label: 'Enable International SMS',
56+
description:
57+
'* To enable it, you need to apply for international templates at the same time.',
58+
type: ConnectorConfigFormItemType.Switch,
59+
required: false,
60+
defaultValue: false,
61+
},
62+
{
63+
key: 'unsupportedCountriesMsg',
64+
label: 'Unsupported Countries Error Message',
65+
description:
66+
'The message to be displayed when the phone number is not supported. If left empty, no error will be returned.',
67+
type: ConnectorConfigFormItemType.Text,
68+
required: false,
69+
defaultValue: 'The administrator has not enabled international SMS services.',
70+
},
71+
],
72+
};
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import nock from 'nock';
2+
3+
import { TemplateType } from '@logto/connector-kit';
4+
5+
import { endpoint } from './constant.js';
6+
import createConnector from './index.js';
7+
import { mockedConfig } from './mock.js';
8+
9+
const getConfig = vi.fn().mockResolvedValue(mockedConfig);
10+
11+
describe('yunpian SMS connector', () => {
12+
it('init without throwing errors', async () => {
13+
await expect(createConnector({ getConfig })).resolves.not.toThrow();
14+
});
15+
16+
describe('sendMessage()', async () => {
17+
const connector = await createConnector({ getConfig });
18+
const { sendMessage } = connector;
19+
20+
beforeAll(() => {
21+
nock.disableNetConnect();
22+
});
23+
24+
afterAll(() => {
25+
nock.enableNetConnect();
26+
});
27+
28+
afterEach(() => {
29+
nock.cleanAll();
30+
});
31+
32+
it('should send message successfully', async () => {
33+
const mockResponse = {
34+
code: 0,
35+
msg: '发送成功',
36+
count: 1,
37+
fee: 0.05,
38+
unit: 'RMB',
39+
mobile: '13800138000',
40+
sid: 3_310_228_982,
41+
};
42+
43+
nock(endpoint).post('').reply(200, mockResponse);
44+
45+
await expect(
46+
sendMessage({
47+
to: '13800138000',
48+
type: TemplateType.Generic,
49+
payload: { code: '1234' },
50+
})
51+
).resolves.not.toThrow();
52+
});
53+
});
54+
});

0 commit comments

Comments
 (0)