闪闪编辑器支持第三方应用集成,您可以通过后端接口和前端嵌入的方式,来将闪闪编辑器集成到您自己的网站里。通过这种方式,您的用户可以无感知的在您的网站里使用闪闪编辑器来制作邮件。
新建邮件
编辑邮件
1、开通账号
点击 闪闪编辑器-应用申请,填写基本信息后,会有运营小妹儿和您联系,开通应用账号。
账号开通之后,点击 闪闪编辑器-应用管理,进入系统。
2、设置固定参数
品牌 logo:将您的网站 logo 显示在闪闪编辑器编辑界面的左上角。
如果不配置此项,将会默认显示闪闪编辑器的 logo <img width="120" src="https://www.shanedit.com/img/index_logo.fc9738f0.png"></img>
此参数在调用 /access/email 接口时也可以传入,以接口传入参数为准。
Push URL:您的用户所编辑的邮件内容会“实时”的推送给这个接口地址。由您进行鉴权(是否由闪闪推送)、校验(内容是否被篡改)之后解析并存储。
Push Key:闪闪会生成密钥对。在推送数据时,闪闪会用公钥加密,您使用私钥解密。
Return URL:您的用户编辑完邮件之后,点击“返回”按钮时,页面所跳转页面的路径。
未来还会增加编辑器界面 UI 的配色等配置项。
URL
xxxxxxxxxx
post https://www.shanedit.com/access/token
描述
在应用内创建用户,获取或更新用户token。
如果id不存在,则是新建用户、获取token;
如果id已存在,则是更新用户token。
用户创建邮件时,需要用对应 token 进行校验。
token 过期,重新获取即可。
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | base64($appid:$appkey) |
Content-Type | header | string | 是 | application/json |
data | body | json | 是 | 用户信息,见示例 |
示例
xxxxxxxxxx
curl -X POST "https://www.shanedit.com/access/token/" \
-H "Authorization: base64($appid:$appkey)" \
-H "Content-Type: application/json" \
--data '
{
"id": "00240",
"name": "user_test01",
"email": "user_test01@sendcloud.net",
"phone": "13477778888"
}
'
id: 必选,是用户的唯一标识
name: 可选;用户名称
email: 可选;用户邮箱
phone: 可选;用户手机号
* 如果id不存在,则闪闪会按照参数,创建新用户,返回token
* 如果id已存在,则闪闪会忽略name,email,phone等字段,只返回已存在用户的信息和新token
返回值示例
xxxxxxxxxx
{
"data": {
"expireTime": "2021-10-15 18:01:08",
"createTime": "2021-10-08 18:04:39",
"id": "00240",
"name": "user_test01",
"email": "user_test01@sendcloud.net",
"phone": "13477778888",
"token": "c41ab1a4-d728-4c1f-954c-8d823769c951"
},
"message": "user login success",
"success": true,
"code": 200
}
expireTime: 有效期默认为7天
id: 回传参数
name: 回传参数,如果为空,系统会生成默认值;如果id已存在,返回已存在用户的name
email: 回传参数;如果id已存在,返回已存在用户的name
phone: 回传参数;如果id已存在,返回已存在用户的name
URL
xxxxxxxxxx
post https://www.shanedit.com/access/code
描述
为前端访问获取临时 code
使用一次即失效,有效期5分钟
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | Bearer $token |
Content-Type | header | string | 是 | application/json |
示例
xxxxxxxxxx
curl -X POST "https://www.shanedit.com/access/code/" \
-H "Authorization: Bearer $token" \
-H "Content-Type: application/json"
返回值示例
xxxxxxxxxx
{
"data": "Clk9YUsQbgU2141iV1o8SDf0fjwipTk1",
"message": "obtained code success",
"success": true,
"code": 200
}
data: 临时code
URL
xxxxxxxxxx
post https://www.shanedit.com/access/email
描述
创建邮件模板
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | Bearer $token |
Content-Type | header | string | 是 | application/json |
data | body | json | 是 | 邮件模板信息,见示例 |
示例
xxxxxxxxxx
curl -X POST "https://www.shanedit.com/access/email/" \
-H "Authorization: Bearer $token" \
-H "Content-Type: application/json" \
--data '
{
"name": "会员30天唤醒邮件1号模板",
"extra": {
"user_tag": "母婴、化妆品",
"user_location": "wuhan"
}
}
'
name: 必选
extra: 可选
返回值示例
xxxxxxxxxx
{
"data": {
"createTime": "2021-10-12 19:24:51",
"extra": {
"user_tag": "母婴、化妆品",
"user_location": "wuhan"
},
"name": "会员30天唤醒邮件1号模板",
"id": "ss_1634037878869"
},
"message": "create email success",
"success": true,
"code": 200
}
id:邮件id
URL
xxxxxxxxxx
get https://www.shanedit.com/access/email/{id}
描述
查询单个邮件模板详情
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | Bearer $token |
示例
xxxxxxxxxx
curl -X GET "https://www.shanedit.com/access/email/250305" \
-H "Authorization: Bearer $token" \
返回值示例
xxxxxxxxxx
{
"data": {
"userId":1,
"html":'<!doctype html><html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office"><head><title></title></head><body>template html source code</body><html>',
"id":250305,
"name":"邮件模板名称",
"settings":'{"type":"root","data":{"bgColor":"#f8f9fa","contColor":"#ffffff"},"placeholders":[],"width":800,"height":457}',
"snapshot":"https://www.shanedit.com/snapshots/f6a977c15c1e5ec0785d6f33650ed7dc.png",
"subject":"邮件模板主题",
"summary":"邮件模板摘要",
"updateTime":"2025-02-26 15:35:23",
"createTime":"2025-02-26 15:35:23"
},
"message": " success",
"success": true,
"code": 200
}
URL
xxxxxxxxxx
delete https://www.shanedit.com/access/email/{id}
描述
删除邮件模板
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | Bearer $token |
示例
xxxxxxxxxx
curl -X DELETE "https://www.shanedit.com/access/email/250305" \
-H "Authorization: Bearer $token" \
返回值示例
xxxxxxxxxx
{
"data": {
},
"message": "delete success",
"success": true,
"code": 200
}
URL
xxxxxxxxxx
put https://www.shanedit.com/access/email/{id}
描述
修改邮件模板,只支持修改模板名称、模板主题
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | Bearer $token |
Content-Type | header | string | 是 | application/json |
data | body | json | 是 | 邮件模板信息,见示例 |
示例
xxxxxxxxxx
curl -X put "https://www.shanedit.com/access/email/77067" \
-H "Authorization: Bearer $token" \
-H "Content-Type: application/json" \
--data '
{
"subject":"会员唤醒2",
"name":"召回2"
}
'
name: 可选
subject:可选
返回值示例
xxxxxxxxxx
{
"data": {
"subject": "会员唤醒2",
"name": "召回2",
"id": 7706
},
"message": "update success",
"success": true,
"code": 200
}
id:邮件id
URL
xxxxxxxxxx
post https://www.shanedit.com/access/email/copy
描述
复制邮件模板
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | Bearer $token |
Content-Type | header | string | 是 | application/j |
data | body | json | 是 | 邮件模板信息,见示例 |
示例
xxxxxxxxxx
curl -X POST "https://www.shanedit.com/access/email/copy" \
-H "Authorization: Bearer $token" \
-H "Content-Type: application/json" \
--data '
{
"id": "ss_1634037878869",
"name": "会员30天唤醒邮件2号模板",
"extra": {
"user_tag": "母婴、化妆品",
"user_location": "wuhan"
}
}
'
id: 必选;想要复制的原邮件id
name: 可选;复制出的新邮件的名称
返回值示例
xxxxxxxxxx
{
"data": {
"createTime": "2021-10-12 19:24:51",
"extra": {
"user_tag": "母婴、化妆品",
"user_location": "wuhan"
}
"name": "会员30天唤醒邮件2号模板",
"id": "ss_1634037878870"
},
"message": "copy email success",
"success": true,
"code": 200
}
id:复制的新邮件的邮件id
name:回传参数,如果为空,系统会生成默认值
URL
xxxxxxxxxx
get https://www.shanedit.com/thirdApp
描述
在您的网站前端调用,跳转到闪闪编辑器,开始编辑邮件
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
code | query | string | 是 | 临时code: $code |
id | query | string | 是 | 邮件id: $id |
lang | query | string | 否 | 语言参数lang: $lang 支持的值: zh_CN,zh_TW, en_US, th_TH, ja_JP |
示例
通过 Javascript 事件打开新标签页
xxxxxxxxxx
<script src="" type="text/javascript">
document.querySelector("#{your html button id}).onclick = function() {
window.open('https://www.shanedit.com/thirdApp?code=$code&id=$id&lang=$lang');
}
</script>
通过 a 标签方式
xxxxxxxxxx
<a href="https://www.shanedit.com/thirdApp?code=$code&id=$id&lang=$lang" target="_blank">编辑邮件</a>
通过 Iframe 内嵌方式
xxxxxxxxxx
<iframe src="https://www.shanedit.com/thirdApp?code=$code&id=$id&lang=$lang" frameborder="0"></iframe>
URL
xxxxxxxxxx
post $PushURL
描述
推送邮件内容
闪闪生成密钥S
闪闪使用密钥S,将邮件内容进行加密,得到DATA
闪闪使用公钥,将密钥S进行加密,得到KEY
闪闪推送$KEY,$DATA到客户的$PushURL
客户使用$PushKey,将得到$KEY进行解密,得到密钥S
客户使用密钥S,将得到的$DATA进行解密,得到原始邮件内容
参数说明
参数 | 分类 | 类型 | 必须 | 说明 |
---|---|---|---|---|
Authorization | header | string | 是 | $KEY |
Content-Type | header | string | 是 | text/plain |
data | body | text | 是 | 加密的邮件内容,见示例 |
示例
xxxxxxxxxx
curl -X POST $PushURL \
-H "Authorization: $KEY" \
-H "Content-Type: text/plain" \
--data $DATA
xxxxxxxxxx
邮件内容加密前的原始格式:
{
"userId": "00240",
"id": "ss_1634037878869",
"name": "会员30天唤醒邮件1号模板",
"subject": "亲亲亲们快来看我们双十一的大优惠",
"summary": "",
"content": "<html lang=\"en\"></html>",
"snapshot": "https://shanedit.ifaxin.com/editor/29/20211014/6fa63fbac71628fd3f70322e59f64ed1.png",
"extra": {
"user_tag": "母婴、化妆品",
"user_location": "wuhan"
},
"createTime": "2021-10-14 14:40:15",
"updateTime": "2021-10-14 16:24:36",
"timestamp": 1634199876966
}
返回值示例
xxxxxxxxxx
客户成功接收并处理,需要返回
1、HTTP 状态码 200
2、返回字符串 "OK"
闪闪接口给 $PushURL 的推送不做失败重试。
xxxxxxxxxx
# coding: utf-8
import requests
import json
import base64
import datetime
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5, AES
APP_ID = ''
APP_KEY = ''
HOST = 'www.shanedit.com'
class Shanshan:
def __init__(self, user_info):
self.user_info = user_info
self.token = None
self.expire_time = None
def _valid_token(self):
if not self.expire_time:
self.auth()
now = datetime.datetime.now()
if now > self.expire_time - datetime.timedelta(hours=1):
self.auth()
def auth(self):
url = f"https://{HOST}/access/token"
auth = str(base64.b64encode((APP_ID + ':' + APP_KEY).encode('utf8')), 'utf8')
headers = {
'Content-Type': 'application/json',
'Authorization': auth
}
response = requests.request("POST", url, headers=headers, data=json.dumps(self.user_info))
self.token = response.json()['data']['token']
self.expire_time = datetime.datetime.strptime(response.json()['data']['expireTime'], '%Y-%m-%d %H:%M:%S')
return self.token
def code(self):
self._valid_token()
url = f"https://{HOST}/access/code"
auth = f'Bearer {self.token}'
headers = {
'Content-Type': 'application/json',
'Authorization': auth
}
response = requests.request("POST", url, headers=headers)
return response.json()['data']
def create(self, email_info):
self._valid_token()
url = f"https://{HOST}/access/email"
auth = f'Bearer {self.token}'
headers = {
'Content-Type': 'application/json',
'Authorization': auth
}
response = requests.request("POST", url, headers=headers, data=json.dumps(email_info))
return response.json()['data']['id']
def copy(self, email_info):
self._valid_token()
url = f"https://{HOST}/access/email/copy"
auth = f'Bearer {self.token}'
headers = {
'Content-Type': 'application/json',
'Authorization': auth
}
response = requests.request("POST", url, headers=headers, data=json.dumps(email_info))
return response.json()['data']['id']
def decrypt_key(self, KEY):
DEFAULT = 128
KEY = base64.b64decode(bytes(KEY, 'utf-8'))
length = len(KEY)
rsa_private_key = RSA.importKey(open('./push_key.pem', 'r').read().strip('\n'))
cipher = PKCS1_v1_5.new(rsa_private_key)
if length < DEFAULT:
decrypt_byte = cipher.decrypt(KEY, 'failure')
else:
offset = 0
res = []
while length - offset > 0:
if length - offset > DEFAULT:
res.append(cipher.decrypt(KEY[offset: offset + DEFAULT], 'failure'))
else:
res.append(cipher.decrypt(KEY[offset:], 'failure'))
offset += DEFAULT
decrypt_byte = b''.join(res)
decrypted = decrypt_byte.decode()
return decrypted
def decrypt_data(self, key, data):
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
enc = base64.b64decode(data)
cipher = AES.new(bytes(key, 'utf-8'), AES.MODE_ECB)
content_b = unpad(cipher.decrypt(enc))
return str(content_b, 'utf-8')
def main():
user_info = {
"id": 244,
"name": "liubida2",
"email": "liubida2@sendcloud.im",
"phone": 13488889999
}
ss = Shanshan(user_info)
email_info = {
"name": "会员30天唤醒邮件2号模板",
"extra": {
"location": "wuhan"
}
}
token = ss.auth()
print(f'获取token:{token}')
print('----')
code = ss.code()
email_id = ss.create(email_info)
print(f'临时code:{code}')
print(f'创建邮件:https://www.shanedit.com/thirdApp?code={code}&id={email_id}')
print('----')
code = ss.code()
print(f'临时code:{code}')
print(f'编辑邮件:https://www.shanedit.com/thirdApp?code={code}&id={email_id}')
print('----')
code = ss.code()
copy_email_id = ss.copy({'id':email_id})
print(f'临时code:{code}')
print(f'copy邮件:https://www.shanedit.com/thirdApp?code={code}&id={copy_email_id}')
# 假定收到如下的KEY、DATA
KEY = ''
DATA = ''
pri_key = ss.decrypt_key(KEY)
email = ss.decrypt_data(pri_key, DATA)
if __name__ == '__main__':
main()
详见:Example.zip