Python Flask X SurveyCake 自動發送優惠碼

前言

在許多行銷活動或顧客調查中,常見的手法是:

  1. 用戶填寫問卷
  2. 系統自動寄送專屬優惠碼

如此一來不僅能回饋填問者,也能提升品牌形象與消費意願。

本篇文章將示範如何透過 Python Flask 整合 SurveyCake 的 Webhook 功能、MailerSend 郵件 API,以及本地優惠碼清單檔案,在問卷提交後自動觸發解密回覆資料、分配優惠碼並寄出客製化郵件的流程。

整合架構概覽

流程說明

  1. 用戶於 SurveyCake 填寫問卷並提交
    SurveyCake 問卷提交後會透過 Webhook 將加密的回覆資料(包含 Email、回答內容)傳送給我們設定的後端 API。
    alt text

  2. 後端 API(Flask)接收 Webhook 並解密資料
    Flask 接收 Webhook 資料後,根據 SurveyCake 提供的 svidhash 向 SurveyCake 取得加密填答結果,透過 AES 解密得到真實問卷答案(JSON 格式)。

  3. 提取 Email 並分配優惠碼
    從填答資料中擷取受訪者的 Email,接著從 coupon.txt 中撈取一組未使用的優惠碼,並將該碼從清單中移除,以確保每位填答者獲得獨一無二的優惠碼。

  4. 透過 MailerSend 寄出包含優惠碼的郵件
    使用 MailerSend API 將定製的 HTML 信件(內含優惠碼與對應條碼)寄給填答者。

使用元件

  • SurveyCake Webhook:自動將填答結果加密後傳至後端。
  • Python Flask:提供簡易的 Web API 接口並處理整個解密、分配優惠碼、寄信的邏輯。
  • MailerSend API:第三方寄信服務,用於發送郵件而不受 Gmail 等每日發信限額影響。
  • AES 加密解密:使用 SurveyCake 提供的金鑰與初始向量,將加密資料解開,取得真實問卷內容。
  • coupon.txt:本地純文字優惠碼清單,一行一碼。

實作範例程式碼

下方為主要程式邏輯的範例程式(簡化後)。實務上請確保金鑰、API Key 等敏感資訊以環境變數或秘密管理來儲存,不直接硬編於程式中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
from flask import Flask, request, jsonify
import requests, json, os, re, base64
from Crypto.Cipher import AES
from jinja2 import Template

app = Flask(__name__)

# SurveyCake 與 MailerSend 相關設定
SURVEYCAKE_DOMAIN = "www.surveycake.com"
HASH_KEY = b"your_hash_key" # SurveyCake 提供
IV_KEY = b"your_iv_key" # SurveyCake 提供

MAILERSEND_API_URL = "https://api.mailersend.com/v1/email"
MAILERSEND_API_KEY = "your_mailersend_api_key"
FROM_EMAIL = "sender@example.com"
FROM_NAME = "優惠代碼發信"
COUPON_FILE = "coupon.txt"

def decrypt_data(encrypted_data):
encrypted = base64.b64decode(encrypted_data)
cipher = AES.new(HASH_KEY, AES.MODE_CBC, IV_KEY)
decrypted_data = cipher.decrypt(encrypted)
return decrypted_data.decode()

def get_coupon():
if not os.path.exists(COUPON_FILE):
raise FileNotFoundError("無可用優惠碼清單")
with open(COUPON_FILE, "r") as f:
lines = f.readlines()
if not lines:
raise ValueError("優惠碼已用罄")
coupon = lines[0].strip()
with open(COUPON_FILE, "w") as f:
f.writelines(lines[1:])
return coupon

def render_email_template(template_path, context):
if not os.path.exists(template_path):
raise FileNotFoundError(f"模板檔案 {template_path} 不存在")
with open(template_path, "r", encoding="utf-8") as file:
template = Template(file.read())
return template.render(context)

def send_email_mailersend(to_email, to_name, subject, html_content):
headers = {
"Authorization": f"Bearer {MAILERSEND_API_KEY}",
"Content-Type": "application/json",
}
data = {
"from": {"email": FROM_EMAIL, "name": FROM_NAME},
"to": [{"email": to_email, "name": to_name}],
"subject": subject,
"html": html_content,
}
response = requests.post(MAILERSEND_API_URL, headers=headers, json=data)
if response.status_code == 202:
print("郵件已成功發送")
else:
print(f"郵件發送失敗: {response.status_code}, {response.text}")

@app.route('/webhook', methods=['POST'])
def webhook():
data = request.form.to_dict()
svid = data.get("svid")
hash_value = data.get("hash")

if not svid or not hash_value:
return jsonify({"status": "error", "message": "缺少必要參數 svid 或 hash"}), 400

# 向 SurveyCake 要求加密資料
query_url = f"https://{SURVEYCAKE_DOMAIN}/webhook/v0/{svid}/{hash_value}"
response = requests.get(query_url)
if response.status_code != 200:
return jsonify({"status": "error", "message": "SurveyCake API 查詢失敗"}), 500

# 解密填答結果
encrypted_data = response.text
decrypted_data = decrypt_data(encrypted_data)
match = re.search(r'(\{.*\})', decrypted_data)
if not match:
return jsonify({"status": "error", "message": "解密後無法取得有效 JSON"}), 500

decrypted_data = json.loads(match.group(1))
results = decrypted_data.get("result", [])
recipient_email = None
for item in results:
if item.get("subject") == "Email":
recipient_email = item.get("answer", [None])[0]
break

if not recipient_email:
return jsonify({"status": "error", "message": "無法取得受訪者 Email"}), 400

# 分配優惠碼
coupon_code = get_coupon()

# 製作信件內容
email_context = {
"title": decrypted_data.get("title"),
"submit_time": decrypted_data.get("submitTime"),
"answers": results,
"coupon_code": coupon_code,
}
html_content = render_email_template("email_template.html", email_context)

# 寄信
send_email_mailersend(recipient_email, "Recipient", f"問卷結果通知 - {email_context['title']}", html_content)
return jsonify({"status": "success"}), 200

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

email_template.html 範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h1>{{ title }}</h1>
<p>提交時間: {{ submit_time }}</p>
<h2>問卷答案</h2>
<ul>
{% for item in answers %}
<li><strong>{{ item.subject }}</strong>: {{ item.answer | join(", ") }}</li>
{% endfor %}
</ul>
<h3>您的專屬優惠碼</h3>
<p>優惠券代碼: <strong>{{ coupon_code }}</strong></p>
<img src="https://barcode.orcascan.com/?type=code128&data={{ coupon_code }}&text={{ coupon_code }}&format=png" height="100px" />
</body>
</html>

使用流程說明

  1. 準備問卷與 Webhook URL

    • 在 SurveyCake 平台建立您的問卷,並在問卷後台設定 Webhook。
    • 將 Webhook URL 指向您部屬此 Flask 專案的公開網址(如 https://your-domain.com/webhook)。
    • 確保您已向 SurveyCake 索取相關的 HASH_KEYIV_KEY,以用於解密加密後的問卷資料。
  2. 部署 Flask 後端服務

    • app.pyemail_template.htmlcoupon.txt 放置於伺服器中。
    • 安裝必要套件(如 requestspycryptodomejinja2Flask)。
    • 將 SurveyCake 所提供的 HASH_KEYIV_KEY,以及 MailerSend 的 MAILERSEND_API_KEYFROM_EMAILFROM_NAME 等資訊正確配置於程式碼中,或改用環境變數。
    • 執行 python3 app.py(或以您喜歡的 WSGI 伺服器方式部署 Flask 應用程式)。
  3. 建立優惠碼清單

    • 編輯 coupon.txt,每行一組優惠碼,如:
      1
      2
      3
      ABC123
      DEF456
      GHI789
    • 當後端收到問卷回覆後,會自動從清單中取出一組優惠碼並刪除該行,確保每個填答者獲得專屬且未重複使用的代碼。
  4. 填寫問卷與觸發 Webhook

    • 受訪者前往 SurveyCake 完成問卷。
    • 問卷提交後,SurveyCake 後端會自動透過 Webhook 將加密的填答結果(包含受訪者 Email)傳送給後端 Flask API。
  5. 後端處理邏輯

    • Flask API 收到 SurveyCake 傳送的 svidhash 後,再次向 SurveyCake 後台取得加密的問卷資料。
    • 使用 AES 解密問卷結果,以 JSON 格式解析出填答者的 Email。
    • coupon.txt 中提取一組可用的優惠碼,並用於後續寄信。
  6. MailerSend 寄信

    • 後端將解密結果(問卷標題、提交時間、回答內容)與取得的優惠碼套入 email_template.html
    • 使用 MailerSend API 將客製化的 HTML 信件寄送給填答者。
    • 信中包含優惠碼與其條碼圖片,填答者可持該代碼至店家兌換相應優惠。
  7. 後續維護與更新

    • 若優惠碼用罄,請更新 coupon.txt 補充新的代碼。
    • 根據行銷需求可調整信件版型 (email_template.html) 或新增其他欄位(如受訪者姓名)。

實務應用與注意事項

  • 優惠碼管理:請定期更新 coupon.txt,確保有足夠的優惠碼。如果優惠碼耗盡,系統將無法正常發放。
  • 安全性
    • 解密金鑰、MailerSend API Key 請勿直接寫於程式中,應使用環境變數或安全管理機制。
    • 部署至生產環境時,請加上 HTTPS 與其他安全措施。

結語

透過上述流程,我們成功串接 SurveyCake 問卷系統、解密回覆資料,並自動向填答者寄出專屬優惠碼。此流程可運用於行銷活動、用戶回饋調查、會員有價問卷調查等情境,提供更佳的使用者體驗與後台管理便利性。

未來可延伸的方向包括:

  • 加入行銷自動化機制,在適當時機寄出提醒信或追加優惠。
  • 將優惠碼與會員系統串接,進一步追蹤使用狀況與行銷成效。

此範例僅為基本架構,請依實際業務需求與規模進行擴充與調整,希望對大家有所幫助 : )