在做退款的時候需要給使用者發一條消息,告訴他已經在退款了,一開始是想用模闆消息來做的,但是看微信開發文檔說模闆消息要下線,推薦用訂閱資訊。這邊記錄一些步驟。
1,登入微信公衆平台擷取訂閱資訊的模闆id
登入微信公衆平台,點選訂閱資訊,我的模闆,添加我的模闆,微信會提供一些常用的模闆,可以選擇,也可以自己申請,通過就可以使用了,把模闆id複制下來儲存一下。代碼裡面需要用到。

2,擷取下發權限
看微信開發文檔用的是這個接口: wx.requestSubscribeMessage ,詳細資訊可以自己檢視微信開發文檔
https://developers.weixin.qq.com/miniprogram/dev/api/open-api/subscribe-message/wx.requestSubscribeMessage.html
下面簡單的看一下代碼的實作:
index.wxml 代碼
<button class="v-btn mt40" bindtap="sendMsg">發送訂閱消息</button>
index.js 代碼 ,簡單介紹一下,tmplIds 參數是在微信公衆平台申請的訂閱資訊模闆id,wx.requestSubscribeMessage 方法是判斷使用者是否授權發訂閱模闆的資訊,同意了就發http請求,請求背景的接口,然後可能是為了安全考慮這個請求的位址必須是https 請求,這個問題可以在微信開發工具裡面設定一下,設定他不要驗證域名;
步驟是,打開微信開發工具--設定--項目設定 ,這個就可以不要驗證域名,真實環境肯定是不推薦這麼做的,但是這樣調試友善。
sendMsg: function (e) {
wx.requestSubscribeMessage({
tmplIds: ["0a-wSlLk6TGc9f8qsar9XtF65qtDh5XiSIhmSGSDrn0"],
success: function (res) {
//成功
console.log(res)
if (res['0a-wSlLk6TGc9f8qsar9XtF65qtDh5XiSIhmSGSDrn0'] === 'accept'){
console.log("使用者同意授權了")
wx.login({
success(res) {
console.log(res.code)
wx.request({
url: 'http://3s.dkys.org:25451/farina-api/subscription/subscription',
method: 'POST',
data: {
code : res.code,
price : 1001,
url : "index.html",
},
header: {
'content-type': 'application/x-www-form-urlencoded', // POST請求
'token': "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHQiOjE1NzU2MDM5NTY5MjcsInVpZCI6IjE4MTI3MDg0ODIiLCJzcmMiOiJBUFAiLCJpYXQiOjE1NzU1OTYxODA5Mjd9.fW_U_MQtovt7d5Nzl7HR1i9pe_y62oTO7gmVU4FrgO4",
},
success(res) {
console.log(res)
}
})
}
})
}else{
console.log("使用者拒絕了======================")
}
},
fail(err) {
//失敗
console.log("這個是失敗的哦=========");
console.error(err);
}
})
}
price,是退款金額,URL,是訂閱資訊傳回使用者點選的回跳頁面,code,就不要介紹了吧,請求頭,content-type ,正常請求頭,token,我們項目的key,
3.調用接口下發訂閱消息
拉下發訂閱資訊接口的代碼我是寫在了後端,
工具包類
package io.farina.MessageSubscription;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class HttpUtil {
private static final CloseableHttpClient httpclient = HttpClients.createDefault();
/**
* 發送HttpGet請求
* @param url
* @return
*/
public static String sendGet(String url) {
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpget);
} catch (IOException e1) {
e1.printStackTrace();
}
String result = null;
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 發送HttpPost請求,參數為map
* @param url
* @param map
* @return
*/
public static String sendPost(String url, Map<String, String> map) {
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : map.entrySet()) {
formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
HttpPost httppost = new HttpPost(url);
httppost.setEntity(entity);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httppost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity1 = response.getEntity();
String result = null;
try {
result = EntityUtils.toString(entity1);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 發送不帶參數的HttpPost請求
* @param url
* @return
*/
public static String sendPost(String url) {
HttpPost httppost = new HttpPost(url);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httppost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity = response.getEntity();
String result = null;
try {
result = EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 向指定 URL 發送POST方法的請求
*
* @param url
* 發送請求的 URL
* @param string
* 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。
* @return 所代表遠端資源的響應結果
*/
public static String sendPost(String url, String string) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打開和URL之間的連接配接
URLConnection conn = realUrl.openConnection();
// 設定通用的請求屬性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 發送POST請求必須設定如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
// 擷取URLConnection對象對應的輸出流
out = new PrintWriter(conn.getOutputStream());
// 發送請求參數
out.print(string);
// flush輸出流的緩沖
out.flush();
// 定義BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("發送 POST 請求出現異常!"+e);
e.printStackTrace();
}
//使用finally塊來關閉輸出流、輸入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
}
package io.farina.MessageSubscription;
public class TemplateParam {
private String key;
private String value;
public TemplateParam(String key,String value){
this.key=key;
this.value=value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
package io.farina.MessageSubscription;
import java.util.List;
public class Template {
private String touser;
private String template_id;
private String page;
private List<TemplateParam> templateParamList;
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
public String getTemplate_id() {
return template_id;
}
public void setTemplate_id(String template_id) {
this.template_id = template_id;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
public List<TemplateParam> getTemplateParamList() {
return templateParamList;
}
public void setTemplateParamList(List<TemplateParam> templateParamList) {
this.templateParamList = templateParamList;
}
public String toJSON() {
StringBuffer buffer = new StringBuffer();
buffer.append("{");
buffer.append(String.format("\"touser\":\"%s\"", this.touser)).append(",");
buffer.append(String.format("\"template_id\":\"%s\"", this.template_id)).append(",");
buffer.append(String.format("\"page\":\"%s\"", this.page)).append(",");
buffer.append("\"data\":{");
TemplateParam param = null;
for (int i = 0; i < this.templateParamList.size(); i++) {
param = templateParamList.get(i);
// 判斷是否追加逗号
if (i < this.templateParamList.size() - 1){
buffer.append(String.format("\"%s\": {\"value\":\"%s\"},", param.getKey(), param.getValue()));
}else{
buffer.append(String.format("\"%s\": {\"value\":\"%s\"}", param.getKey(), param.getValue()));
}
}
buffer.append("}");
buffer.append("}");
return buffer.toString();
}
}
package io.farina.MessageSubscription;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* 實作了 MyX509TrustManager 接口
* @author admin
*
*/
public class MyX509TrustManager implements X509TrustManager {
// 檢查用戶端證書
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 檢查伺服器端證書
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 傳回受信任的X509證書數組
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
下發訂閱資訊接口
package io.farina.MessageSubscription;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
/****
* 退款提示
* @author admin
*
*/
@RestController
@RequestMapping("subscription")
public class MessageSubscriptionController2 {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
/***
* 擷取ACCESS_TOKEN
* @return
*/
public String subscroption() {
String appid="微信小程式appid";
String secret="微信小程式secret";
String url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+ appid+ "&secret="+ secret;
String results = HttpUtil.sendGet(url);
JSONObject jsonObject =JSONObject.parseObject(results);
String Access_Token=jsonObject.get("access_token").toString();
return Access_Token;
}
/****
* 擷取opneid和sessionKey
*
* @param code
* @return
* @throws Exception
*/
public JSONObject getSessionKeyOrOpenId(String code) throws Exception {
// 微信端登入code
String wxCode = code;
// 請求路徑
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
String appid="小程式appid";
String secret="小程式secret";
//b2d42c4f39cf71f60416546cdb2855e1
String requestUrlParam = "appid="+appid+"&secret="+secret+"&js_code=" + wxCode
+ "&grant_type=authorization_code";
// Java發post請求
//HttpUtil.sendPost(url, map);
JSONObject jsonObject = JSON.parseObject(HttpUtil.sendPost(requestUrl, requestUrlParam));
if (jsonObject.get("errcode")!=null) {
//logger.error("code擷取oppid和sessionkey 異常,檢查小程式配置是否正确");
throw new Exception("異常資訊:"+jsonObject.get("errmsg").toString());
}
return jsonObject;
}
/****
* 退款資訊訂閱
* @param price 提款金額
* @param url 小程式倉庫的位址
* @param code code
*/
@RequestMapping(value = "/subscription", method = RequestMethod.POST)
public void sund(
@RequestParam(value = "price", required = false) String price,
@RequestParam(value = "url", required = true) String url,
@RequestParam(value = "code", required = true) String code) {
log.info("請求提款接口參數,提款金額:[{}],傳回位址:[{}],code:[{}]",price,url,code);
//擷取openid
Map<String, Object> map = new HashMap<String, Object>();
com.alibaba.fastjson.JSONObject SessionKeyOpenId;
String openid="";
try {
SessionKeyOpenId =getSessionKeyOrOpenId(code);
openid = SessionKeyOpenId.getString("openid");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String access_Token=subscroption();
Template template=new Template();
//訂閱消息 子產品id,
template.setTemplate_id("訂閱資訊模闆id");
//openId
template.setTouser(openid);
//這個是訂閱資訊點進去的時候進去的界面,
template.setPage(url);
List<TemplateParam> paras=new ArrayList<TemplateParam>();
//這些參數在/訂閱資訊/詳情/詳細資訊裡面找 ,把後面的 {{thing5.DATA}} 取 thing5
paras.add(new TemplateParam("thing5","預計1~5工作日到微信錢包,請注意查收"));
paras.add(new TemplateParam("amount1",price));
paras.add(new TemplateParam("thing4","提款到微信錢包"));
template.setTemplateParamList(paras);
//給訂閱的使用者發資訊的位址
String requestUrl="https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN";
//替換 ACCESS_TOKEN
requestUrl=requestUrl.replace("ACCESS_TOKEN", access_Token);
//使用者授權後給使用者發訂閱的資訊,使用者授權在前端處理
JSONObject jsonResult=httpsRequest(requestUrl, "POST", template.toJSON());
int newcode=Integer.parseInt(jsonResult.get("errcode").toString());
if (jsonResult.get("errmsg").equals("ok")&&newcode==0) {
log.info("訂閱資訊發送成功====");
}
log.info("訂閱資訊發送失敗====");
}
public JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 建立SSLContext對象,并使用我們指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 從上述SSLContext對象中得到SSLSocketFactory對象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 設定請求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod)) {
httpUrlConn.connect();
}
// 當有資料需要送出時
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意編碼格式,防止中文亂碼
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将傳回的輸入流轉換成字元串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 釋放資源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject= JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
ce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return jsonObject;
}
}
ok,代碼大概就是這個樣子