├── .gitignore ├── LICENSE ├── README.md ├── README_en.md ├── SECURITY.md ├── images ├── demo.PNG ├── server_diagram.PNG └── server_diagram_en.PNG ├── index.js ├── index.ts ├── package.json ├── public └── js │ ├── demo.js │ └── demo.ts ├── tsconfig.json ├── types └── wechat.d.ts └── views └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | node_modules 3 | .vscode 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English version](README_en.md) 2 | 3 | 本教程将覆盖了基本的在使用[微信JSSDK](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html)的网页上运用TypeScript的例子。 4 | 5 | # 开始之前 6 | 7 | 1. 安装[node.js](https://nodejs.org/en/download/)。 8 | 9 | 1. 拥有/申请网页将使用的域名以及能够使用Node.js的服务器。可参照[Create a Node.js Application on Web App](https://docs.microsoft.com/en-us/azure/app-service-web/app-service-web-get-started-nodejs)文档使用Azure。 10 | 11 | 2. 完成微信开发文档[步骤一:绑定域名](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E6.AD.A5.E9.AA.A4.E4.B8.80.EF.BC.9A.E7.BB.91.E5.AE.9A.E5.9F.9F.E5.90.8D)。如果没有自己的公众号,可注册并使用微信[测试号](https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login)。记录下自己的appId, appsecret以及将使用的host URL。 12 | 13 | # 搭建node.js/express后台 14 | 15 | ## 初始 16 | 17 | 建立一个新的npm package, 18 | 19 | ``` 20 | mkdir wxapp 21 | cd wxapp 22 | npm init 23 | ``` 24 | 25 | 在生成的`package.json`的`scripts`中添加以下scripts, 26 | 27 | ``` 28 | "scripts": { 29 | "start": "node index.js", 30 | "build-ts": "node ./node_modules/typescript/bin/tsc" 31 | }, 32 | ``` 33 | 34 | 安装需要的packages(express, ejs, request以及sha1), 35 | 36 | ``` 37 | npm install --save express ejs request sha1 38 | ``` 39 | 40 | 安装TypeScript以及之前安装的packages的类型定义。 41 | 42 | ``` 43 | npm install --save-dev typescript @types/node @types/express @types/request @types/sha1 44 | ``` 45 | 46 | 由于暂时[DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped)中并没有JSSDK相关的类型定义文件(.d.ts),请将本项目中的`types`文件夹(包含类型定义文件[wechat.d.ts](types/wechat.d.ts))复制到根目录(`wxapp`)中以便TypeScript获取JSSDK的类型定义。 47 | 48 | ## 配置TypeScript 49 | 50 | 在`wxapp`根目录下添加TypeScript配置文件`tsconfig.json`, 51 | 52 | ```js 53 | { 54 | "compilerOptions": { 55 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 56 | "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */ 57 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 58 | } 59 | } 60 | ``` 61 | 62 | 可以根据项目的需求自行添加[其他编译选项](https://www.typescriptlang.org/docs/handbook/compiler-options.html),比如`strict`。 63 | 64 | ## 获取jsapi_ticket 65 | 66 | 对应[通过config接口注入权限验证配置](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E6.AD.A5.E9.AA.A4.E4.B8.89.EF.BC.9A.E9.80.9A.E8.BF.87config.E6.8E.A5.E5.8F.A3.E6.B3.A8.E5.85.A5.E6.9D.83.E9.99.90.E9.AA.8C.E8.AF.81.E9.85.8D.E7.BD.AE)文档,调用微信JSSDK需要在自己的服务器后台向微信服务器获取jsapi_ticket并在前端通过`wx.config`进行验证。大致实现流程, 67 | 68 |

69 | 70 |

71 | 72 | 在根目录添加后台的初始文件`index.ts`, 73 | 74 | ```ts 75 | import * as express from "express"; 76 | import * as request from "request"; 77 | import sha1 = require("sha1"); 78 | 79 | let app = express(); 80 | 81 | // Insert metadata 82 | let appId = ''; // Insert your appId 83 | let appsecret = ''; // insert your appsecret 84 | let url = ''; // insert host url, e.g. http://wxapp.azurewebsites.net/ 85 | let nonceStr = ''; // insert any string 86 | 87 | // define an interface containing params for wx.config 88 | interface configObj { 89 | appId: string, 90 | timestamp: string, 91 | nonceStr: string, 92 | signature: string 93 | } 94 | 95 | // handshake with WeChat server and get signature for wx.config 96 | function getWXConfig(cb) { 97 | request.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+appId+'&secret='+appsecret, (err, res, body) => { 98 | request.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+JSON.parse(body).access_token+'&type=jsapi', (err, res, body) => { 99 | let ticket = JSON.parse(body).ticket; 100 | let o: configObj = { 101 | appId: appId, 102 | nonceStr: nonceStr, 103 | timestamp: new Date().getTime() / 1000 + '', 104 | signature: '' 105 | }; 106 | o.signature = sha1('jsapi_ticket='+ticket+'&noncestr='+o.nonceStr+'×tamp='+o.timestamp+'&url='+url).toString(); 107 | cb(o); 108 | }); 109 | }); 110 | } 111 | 112 | app.engine('.html', require('ejs').__express); // set up ejs as view engine 113 | app.set('views', __dirname + '/views'); // set views dir 114 | app.set('view engine', 'html'); // use .html for ejs files 115 | app.use(express.static('public')) // expose assets in /public 116 | app.get('/', function (req, res) { 117 | getWXConfig(config => { 118 | // handshake with WeChat server and render index.html with the returned signature 119 | res.render('index', { 120 | appId: config.appId, 121 | timestamp: config.timestamp, 122 | nonceStr: config.nonceStr, 123 | signature: config.signature, 124 | }); 125 | }) 126 | }); 127 | app.listen(8080); 128 | 129 | ``` 130 | 131 | 在`index.ts`中修改并填入自己的appId等等参数, 132 | ```ts 133 | // Insert metadata 134 | let appId = ''; // Insert your appId 135 | let appsecret = ''; // insert your appsecret 136 | let url = ''; // insert host url, e.g. http://wxapp.azurewebsites.net/ 137 | let nonceStr = 'hellotypescript'; // insert any string 138 | ``` 139 | 140 | 建立的node.js+express后台会通过`getWXConfig`向微信服务器[获取jsapi_ticket](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E8.8E.B7.E5.8F.96api_ticket),并将[wx.config](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E6.AD.A5.E9.AA.A4.E4.B8.89.EF.BC.9A.E9.80.9A.E8.BF.87config.E6.8E.A5.E5.8F.A3.E6.B3.A8.E5.85.A5.E6.9D.83.E9.99.90.E9.AA.8C.E8.AF.81.E9.85.8D.E7.BD.AE)所需的参数通过ejs渲染(`res.render(...)`)至客户端页面。 141 | 142 | 在`index.ts`中已定义将后台推至客户端的文件放入`/views`(包含所有需要ejs渲染的`html`文件)以及`/public`(其余的文件)文件夹。之后的步骤将覆盖前端的页面。 143 | ``` ts 144 | app.set('views', __dirname + '/views'); // set views dir 145 | app.use(express.static('public')) // expose assets in /public 146 | ``` 147 | 148 | # 客户端页面 149 | 150 | ## index.html 151 | 152 | 在根目录下建立`views`文件夹并在其中添加`index.html`(推至客户端的主页), 153 | 154 | ```html 155 | 156 | 157 | 158 | 159 | WeChat TypeScript sample 160 | 161 | 162 | 163 | 210 | 211 | 212 | 213 | ``` 214 | 215 | `index.html`中引入了微信JSSDK(`http://res.wx.qq.com/open/js/jweixin-1.0.0.js`)以及将在客户端实现的简单的demo(`/js/demo.js`)。嵌入的JavaScript包含了之前渲染的`getWXConfig`提供的讯息并通过`wx.config`注入权限验证配置。 216 | 217 | ## demo.ts 218 | 219 | 在根目录下建立`public/js`文件夹,并在其中添加`demo.ts`, 220 | 221 | ```ts 222 | wx.ready(() => { 223 | // open specifc location on map 224 | wx.openLocation({ 225 | latitude: 0, 226 | longitude: 0, 227 | name: '', 228 | address: '', 229 | scale: 1, 230 | infoUrl: '' 231 | }); 232 | }) 233 | 234 | wx.error((err) => alert(err)); 235 | ``` 236 | 237 | 在这个简化的例子中,我们仅使用`wx.openLocation`打开地图,但在`demo.ts`中你可以尝试使用任何在`JsApiList`中申请的API。 238 | 239 | # 生成与部署 240 | 241 | 将`.ts`文件编译成`.js`, 242 | 243 | ``` 244 | npm run build-ts 245 | ``` 246 | 247 | 部署至服务器。 248 | 249 | 将页面所在的网址转换成二维码,打开微信扫一扫便可看到成果。 250 | 251 |

252 | 253 |

254 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | [中文版](README.md) (Chinese Version of this walkthrough) 2 | 3 | This tutorial covers a basic sample using [WeChat JSSDK](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html) and TypeScript together. 4 | 5 | # Before you start 6 | 7 | 1. Install [node.js](https://nodejs.org/en/download/). 8 | 9 | 1. Get your own domain and node.js server. See [Create a Node.js Application on Web App](https://docs.microsoft.com/en-us/azure/app-service-web/app-service-web-get-started-nodejs) for using Azure. 10 | 11 | 2. Finish [step 1 - binding domain name](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E6.AD.A5.E9.AA.A4.E4.B8.80.EF.BC.9A.E7.BB.91.E5.AE.9A.E5.9F.9F.E5.90.8D). If you don't have an official WeChat account, you can apply for a [test account](https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login). Keep your appId, appsecret and host URL at hand. 12 | 13 | # Set up node.js/express 14 | 15 | ## Initial steps 16 | 17 | Create a new package, 18 | 19 | ``` 20 | mkdir wxapp 21 | cd wxapp 22 | npm init 23 | ``` 24 | 25 | In `scripts` in `package.json`, add, 26 | 27 | ``` 28 | "scripts": { 29 | "start": "node index.js", 30 | "build-ts": "node ./node_modules/typescript/bin/tsc" 31 | }, 32 | ``` 33 | 34 | Install dependencies (express, ejs, request, and sha1), 35 | 36 | ``` 37 | npm install --save express ejs request sha1 38 | ``` 39 | 40 | Install TypeScript and type definition files for installed packages, 41 | 42 | ``` 43 | npm install --save-dev typescript @types/node @types/express @types/request @types/sha1 44 | ``` 45 | 46 | Since [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) does not have type definitions for WeChat JSSDK at the mement , copy the `types` folder containing [wechat.d.ts](/types/wechat.d.ts) to the project root folder (`wxapp`). 47 | 48 | ## Configure TypeScript 49 | 50 | Add TypeScript configuration file `tsconfig.json` under the project root folder, 51 | 52 | ```js 53 | { 54 | "compilerOptions": { 55 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 56 | "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */ 57 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 58 | } 59 | } 60 | ``` 61 | 62 | You can add [other compiler flags](https://www.typescriptlang.org/docs/handbook/compiler-options.html) (e.g. `strict`) based on your own need. 63 | 64 | ## Get jsapi_ticket 65 | 66 | According to [WeChat documentation](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E6.AD.A5.E9.AA.A4.E4.B8.89.EF.BC.9A.E9.80.9A.E8.BF.87config.E6.8E.A5.E5.8F.A3.E6.B3.A8.E5.85.A5.E6.9D.83.E9.99.90.E9.AA.8C.E8.AF.81.E9.85.8D.E7.BD.AE), using WeChat JSSDK requires getting jsapi_ticket from WeChat server on our server side and then using `wx.config` to get API permission on the cliend side. A rough flow looks like below, 67 | 68 |

69 | 70 |

71 | 72 | Add `index.ts` in the root folder, 73 | 74 | ```ts 75 | import * as express from "express"; 76 | import * as request from "request"; 77 | import sha1 = require("sha1"); 78 | 79 | let app = express(); 80 | 81 | // Insert metadata 82 | let appId = ''; // Insert your appId 83 | let appsecret = ''; // insert your appsecret 84 | let url = ''; // insert host url, e.g. http://wxapp.azurewebsites.net/ 85 | let nonceStr = ''; // insert any string 86 | 87 | // define an interface containing params for wx.config 88 | interface configObj { 89 | appId: string, 90 | timestamp: string, 91 | nonceStr: string, 92 | signature: string 93 | } 94 | 95 | // handshake with WeChat server and get signature for wx.config 96 | function getWXConfig(cb) { 97 | request.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+appId+'&secret='+appsecret, (err, res, body) => { 98 | request.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+JSON.parse(body).access_token+'&type=jsapi', (err, res, body) => { 99 | let ticket = JSON.parse(body).ticket; 100 | let o: configObj = { 101 | appId: appId, 102 | nonceStr: nonceStr, 103 | timestamp: new Date().getTime() / 1000 + '', 104 | signature: '' 105 | }; 106 | o.signature = sha1('jsapi_ticket='+ticket+'&noncestr='+o.nonceStr+'×tamp='+o.timestamp+'&url='+url).toString(); 107 | cb(o); 108 | }); 109 | }); 110 | } 111 | 112 | app.engine('.html', require('ejs').__express); // set up ejs as view engine 113 | app.set('views', __dirname + '/views'); // set views dir 114 | app.set('view engine', 'html'); // use .html for ejs files 115 | app.use(express.static('public')) // expose assets in /public 116 | app.get('/', function (req, res) { 117 | getWXConfig(config => { 118 | // handshake with WeChat server and render index.html with the returned signature 119 | res.render('index', { 120 | appId: config.appId, 121 | timestamp: config.timestamp, 122 | nonceStr: config.nonceStr, 123 | signature: config.signature, 124 | }); 125 | }) 126 | }); 127 | app.listen(8080); 128 | 129 | ``` 130 | 131 | Fill in your appId and other metadata in `index.ts`, 132 | 133 | ```ts 134 | // Insert metadata 135 | let appId = ''; // Insert your appId 136 | let appsecret = ''; // insert your appsecret 137 | let url = ''; // insert host url, e.g. http://wxapp.azurewebsites.net/ 138 | let nonceStr = 'hellotypescript'; // insert any string 139 | ``` 140 | 141 | Our server will [get jsapi_ticket](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E8.8E.B7.E5.8F.96api_ticket) from WeChat server through `getWXConfig`, and render the signature for [wx.config](https://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E6.AD.A5.E9.AA.A4.E4.B8.89.EF.BC.9A.E9.80.9A.E8.BF.87config.E6.8E.A5.E5.8F.A3.E6.B3.A8.E5.85.A5.E6.9D.83.E9.99.90.E9.AA.8C.E8.AF.81.E9.85.8D.E7.BD.AE) in client side html through ejs. 142 | 143 | Within `index.ts`, we've defined our client side files to be stored in `/views` (`html` to be rendered by ejs) and `/public` (other files). The next step will cover client side pages. 144 | 145 | ``` ts 146 | app.set('views', __dirname + '/views'); // set views dir 147 | app.use(express.static('public')) // expose assets in /public 148 | ``` 149 | 150 | # Client side pages 151 | 152 | ## index.html 153 | 154 | Create `views` folder under the root folder and add `index.html`, 155 | 156 | ```html 157 | 158 | 159 | 160 | 161 | WeChat TypeScript sample 162 | 163 | 164 | 165 | 212 | 213 | 214 | 215 | ``` 216 | 217 | `index.html` includes the JSSDK (`http://res.wx.qq.com/open/js/jweixin-1.0.0.js`) and a simple demo (`/js/demo.js`). The embedded JavaScript includes rendered signature from `getWXConfig` and uses `wx.config` to get API permission from WeChat. 218 | 219 | ## demo.ts 220 | 221 | Create `public/js` folder under the root folder, and add `demo.ts`, 222 | 223 | ```ts 224 | wx.ready(() => { 225 | // open specifc location on map 226 | wx.openLocation({ 227 | latitude: 0, 228 | longitude: 0, 229 | name: '', 230 | address: '', 231 | scale: 1, 232 | infoUrl: '' 233 | }); 234 | }) 235 | 236 | wx.error((err) => alert(err)); 237 | ``` 238 | 239 | In this simple demo, we only use `wx.openLocation` to open a map, but you can try calling any API from `JsApiList` in `demo.ts`. 240 | 241 | # Build and deploy 242 | 243 | Transpile `.ts` files to `.js`, 244 | 245 | ``` 246 | npm run build-ts 247 | ``` 248 | 249 | Deploy the project to server. 250 | 251 | After deployment, translate your URL to QR code and open 微信扫一扫 to see the demo in action. 252 | 253 |

254 | 255 |

256 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /images/demo.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/TypeScript-WeChat-Starter/51e9a265828efd3140b162445084374604f2f72f/images/demo.PNG -------------------------------------------------------------------------------- /images/server_diagram.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/TypeScript-WeChat-Starter/51e9a265828efd3140b162445084374604f2f72f/images/server_diagram.PNG -------------------------------------------------------------------------------- /images/server_diagram_en.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/TypeScript-WeChat-Starter/51e9a265828efd3140b162445084374604f2f72f/images/server_diagram_en.PNG -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const express = require("express"); 4 | const request = require("request"); 5 | const sha1 = require("sha1"); 6 | let app = express(); 7 | // Insert metadata 8 | let appId = ''; // Insert your appId 9 | let appsecret = ''; // insert your appsecret 10 | let url = ''; // insert host url, e.g. http://wxapp.azurewebsites.net/ 11 | let nonceStr = ''; // insert any string 12 | // handshake with WeChat server and get signature for wx.config 13 | function getWXConfig(cb) { 14 | request.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + appId + '&secret=' + appsecret, (err, res, body) => { 15 | request.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + JSON.parse(body).access_token + '&type=jsapi', (err, res, body) => { 16 | let ticket = JSON.parse(body).ticket; 17 | let o = { 18 | appId: appId, 19 | nonceStr: nonceStr, 20 | timestamp: new Date().getTime() / 1000 + '', 21 | signature: '' 22 | }; 23 | o.signature = sha1('jsapi_ticket=' + ticket + '&noncestr=' + o.nonceStr + '×tamp=' + o.timestamp + '&url=' + url).toString(); 24 | cb(o); 25 | }); 26 | }); 27 | } 28 | app.engine('.html', require('ejs').__express); // set up ejs as view engine 29 | app.set('views', __dirname + '/views'); // set views dir 30 | app.set('view engine', 'html'); // use .html for ejs files 31 | app.use(express.static('public')); // expose assets in /public 32 | app.get('/', function (req, res) { 33 | getWXConfig(config => { 34 | // handshake with WeChat server and render index.html with the returned signature 35 | res.render('index', { 36 | appId: config.appId, 37 | timestamp: config.timestamp, 38 | nonceStr: config.nonceStr, 39 | signature: config.signature, 40 | }); 41 | }); 42 | }); 43 | app.listen(8080); 44 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import * as express from "express"; 2 | import * as request from "request"; 3 | import sha1 = require("sha1"); 4 | 5 | let app = express(); 6 | 7 | // Insert metadata 8 | let appId = ''; // Insert your appId 9 | let appsecret = ''; // insert your appsecret 10 | let url = ''; // insert host url, e.g. http://wxapp.azurewebsites.net/ 11 | let nonceStr = ''; // insert any string 12 | 13 | // define an interface containing params for wx.config 14 | interface configObj { 15 | appId: string, 16 | timestamp: string, 17 | nonceStr: string, 18 | signature: string 19 | } 20 | 21 | // handshake with WeChat server and get signature for wx.config 22 | function getWXConfig(cb) { 23 | request.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+appId+'&secret='+appsecret, (err, res, body) => { 24 | request.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+JSON.parse(body).access_token+'&type=jsapi', (err, res, body) => { 25 | let ticket = JSON.parse(body).ticket; 26 | let o: configObj = { 27 | appId: appId, 28 | nonceStr: nonceStr, 29 | timestamp: new Date().getTime() / 1000 + '', 30 | signature: '' 31 | }; 32 | o.signature = sha1('jsapi_ticket='+ticket+'&noncestr='+o.nonceStr+'×tamp='+o.timestamp+'&url='+url).toString(); 33 | cb(o); 34 | }); 35 | }); 36 | } 37 | 38 | app.engine('.html', require('ejs').__express); // set up ejs as view engine 39 | app.set('views', __dirname + '/views'); // set views dir 40 | app.set('view engine', 'html'); // use .html for ejs files 41 | app.use(express.static('public')) // expose assets in /public 42 | app.get('/', function (req, res) { 43 | getWXConfig(config => { 44 | // handshake with WeChat server and render index.html with the returned signature 45 | res.render('index', { 46 | appId: config.appId, 47 | timestamp: config.timestamp, 48 | nonceStr: config.nonceStr, 49 | signature: config.signature, 50 | }); 51 | }) 52 | }); 53 | app.listen(8080); 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat-typescript-sample", 3 | "description": "Sample for developing with wechat JSSDK and TypeScript", 4 | "version": "0.0.1", 5 | "private": true, 6 | "license": "MIT", 7 | "author": "Microsoft", 8 | "engines": { 9 | "node": ">=6.9.1" 10 | }, 11 | "scripts": { 12 | "start": "node index.js", 13 | "build-ts": "node ./node_modules/typescript/bin/tsc" 14 | }, 15 | "dependencies": { 16 | "ejs": "^2.5.6", 17 | "express": "^4.15.2", 18 | "request": "^2.81.0", 19 | "sha1": "^1.1.1" 20 | }, 21 | "devDependencies": { 22 | "@types/express": "^4.0.35", 23 | "@types/node": "^7.0.18", 24 | "@types/request": "2.0.7", 25 | "@types/sha1": "^1.1.0", 26 | "typescript": "^2.3.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /public/js/demo.js: -------------------------------------------------------------------------------- 1 | wx.ready(() => { 2 | // open specifc location on map 3 | wx.openLocation({ 4 | latitude: 0, 5 | longitude: 0, 6 | name: '', 7 | address: '', 8 | scale: 1, 9 | infoUrl: '' 10 | }); 11 | }); 12 | wx.error((err) => alert(err)); 13 | -------------------------------------------------------------------------------- /public/js/demo.ts: -------------------------------------------------------------------------------- 1 | wx.ready(() => { 2 | // open specifc location on map 3 | wx.openLocation({ 4 | latitude: 0, 5 | longitude: 0, 6 | name: '', 7 | address: '', 8 | scale: 1, 9 | infoUrl: '' 10 | }); 11 | }) 12 | 13 | wx.error((err) => alert(err)); 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 4 | "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */ 5 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 6 | } 7 | } -------------------------------------------------------------------------------- /types/wechat.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace wx { 2 | function addCard(a: any): any; 3 | function checkJsApi(a: any): any; 4 | function chooseCard(a: any): any; 5 | function chooseImage(a: any): any; 6 | function chooseWXPay(a: any): void; 7 | function closeWindow(a: any): void; 8 | function config(a: any): any; 9 | function downloadImage(a: any): void; 10 | function downloadVoice(a: any): void; 11 | function error(a: any): void; 12 | function getLocation(a: any): any; 13 | function getNetworkType(a: any): any; 14 | function hideAllNonBaseMenuItem(a: any): void; 15 | function hideMenuItems(a: any): void; 16 | function hideOptionMenu(a: any): void; 17 | function onMenuShareAppMessage(a: any): void; 18 | function onMenuShareQQ(a: any): void; 19 | function onMenuShareQZone(a: any): void; 20 | function onMenuShareTimeline(a: any): void; 21 | function onMenuShareWeibo(a: any): void; 22 | function onVoicePlayEnd(a: any): void; 23 | function onVoiceRecordEnd(a: any): void; 24 | function openCard(a: any): void; 25 | function openLocation(a: any): void; 26 | function openProductSpecificView(a: any): void; 27 | function pauseVoice(a: any): void; 28 | function playVoice(a: any): void; 29 | function previewImage(a: any): void; 30 | function ready(a: any): void; 31 | function scanQRCode(a: any): any; 32 | function showAllNonBaseMenuItem(a: any): void; 33 | function showMenuItems(a: any): void; 34 | function showOptionMenu(a: any): void; 35 | function startRecord(a: any): void; 36 | function stopRecord(a: any): void; 37 | function stopVoice(a: any): void; 38 | function translateVoice(a: any): void; 39 | function uploadImage(a: any): void; 40 | function uploadVoice(a: any): void; 41 | } 42 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WeChat TypeScript sample 6 | 7 | 8 | 9 | 56 | 57 | 58 | 59 | --------------------------------------------------------------------------------