├── app.js
├── components
└── payjs
│ ├── payjs.json
│ ├── payjs.wxml
│ ├── payjs.wxss
│ └── payjs.js
├── pages
└── index
│ ├── index.json
│ ├── index.wxss
│ ├── index.wxml
│ └── index.js
├── .gitignore
├── app.wxss
├── app.json
├── project.config.json
├── utils
├── util.js
└── md5.js
└── README.md
/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({})
--------------------------------------------------------------------------------
/components/payjs/payjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {
3 | "payjs": "/components/payjs/payjs"
4 | }
5 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows
2 | [Dd]esktop.ini
3 | Thumbs.db
4 | $RECYCLE.BIN/
5 |
6 | # macOS
7 | .DS_Store
8 | .fseventsd
9 | .Spotlight-V100
10 | .TemporaryItems
11 | .Trashes
12 |
13 | # Node.js
14 | node_modules/
15 |
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
1 | button {
2 | background: none;
3 | }
4 |
5 | button::after {
6 | border: none;
7 | }
8 |
9 | .row {
10 | display: flex;
11 | flex-direction: row;
12 | align-items: center;
13 | }
14 |
15 | .row .left {
16 | flex-grow: 1;
17 | }
18 |
19 | .hr {
20 | height: 0;
21 | border-top: solid 1px #d9d9d9;
22 | margin: 20rpx 0;
23 | }
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages":[
3 | "pages/index/index"
4 | ],
5 | "window":{
6 | "backgroundTextStyle":"light",
7 | "navigationBarBackgroundColor": "#fff",
8 | "navigationBarTitleText": "PAYJS 小程序支付测试",
9 | "navigationBarTextStyle":"black"
10 | },
11 | "navigateToMiniProgramAppIdList": [
12 | "wx959c8c1fb2d877b5"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/components/payjs/payjs.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 支付需要跳转到第三方平台进行
4 | 确认跳转
5 |
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 150rpx 20rpx;
3 | }
4 |
5 | .row {
6 | font-size: 30rpx;
7 | line-height: 1.8;
8 | color: #888888;
9 | }
10 |
11 | .button {
12 | position: fixed;
13 | top: 50vh;
14 | left: 275rpx;
15 | width: 200rpx;
16 |
17 | border: solid 1px #1AAD19;
18 | color: #1AAD19;
19 | border-radius: 10rpx;
20 |
21 | font-size: 32rpx;
22 | }
23 |
24 | .button.button-refresh {
25 | left: 225rpx;
26 | width: 300rpx;
27 | }
--------------------------------------------------------------------------------
/components/payjs/payjs.wxss:
--------------------------------------------------------------------------------
1 | .bg {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | width: 100vw;
6 | height: 100vh;
7 |
8 | background-color: black;
9 | opacity: 0.5;
10 | }
11 |
12 | .button {
13 | background: none;
14 | }
15 |
16 | .button::after {
17 | border: none;
18 | }
19 |
20 | .modal {
21 | position: fixed;
22 | left: 10vw;
23 | top: 30vh;
24 | width: 80vw;
25 | height: 20vh;
26 |
27 | background-color: white;
28 | border-radius: 5rpx;
29 |
30 | text-align: center;
31 | line-height: 10vh;
32 | }
33 |
34 | .modal .content {
35 | height: 10vh;
36 | color: #9d9d9d;
37 | font-size: 28rpx;
38 | }
39 |
40 | .modal .button {
41 | height: 10vh;
42 | color: #3cc51f;
43 | font-size: 36rpx;
44 | }
--------------------------------------------------------------------------------
/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true,
12 | "uglifyFileName": false
13 | },
14 | "compileType": "miniprogram",
15 | "libVersion": "2.4.0",
16 | "appid": "wx70688e9c6ca4264b",
17 | "projectname": "PAYJS%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%95",
18 | "debugOptions": {
19 | "hidedInDevtools": []
20 | },
21 | "isGameTourist": false,
22 | "condition": {
23 | "search": {
24 | "current": -1,
25 | "list": []
26 | },
27 | "conversation": {
28 | "current": -1,
29 | "list": []
30 | },
31 | "game": {
32 | "currentL": -1,
33 | "list": []
34 | },
35 | "miniprogram": {
36 | "current": -1,
37 | "list": []
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 订单标题
6 | {{ orderParams.body }}
7 |
8 |
9 | 支付金额
10 | {{ orderParams.total_fee / 100 }} 元
11 |
12 |
13 | 订单号
14 | {{ orderParams.out_trade_no }}
15 |
16 |
17 |
18 | 生成订单使用的随机数
19 | {{ orderParams.nonce }}
20 |
21 |
22 | 异步通知地址
23 | {{ orderParams.notify_url || '无' }}
24 |
25 |
26 | 异步通知数据
27 | {{ orderParams.attach ? '有' : '无' }}
28 |
29 |
30 |
31 |
32 |
33 |
34 | 支付状态
35 | 支付成功
36 |
37 |
38 | PAYJS 订单号
39 | {{ orderResults.payjsOrderId }}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/utils/util.js:
--------------------------------------------------------------------------------
1 | // 在下面设置商户号
2 | const mchid = '1511214851'
3 |
4 | // 在下面设置密钥
5 | // 特别注意:此项设置应该仅供测试,生产环境下请在后端完成签名,切忌在小程序内暴露商户密钥
6 | const secret = ''
7 |
8 | const getRandomNumber = (minNum = 100000, maxNum = 999999) => parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
9 |
10 | const getSign = obj => {
11 | /*
12 | * 签名算法
13 | *
14 | * 由于密钥不应该在小程序内出现,因此生产环境下的小程序不应该包含此参数
15 | */
16 |
17 | let keys = Object.keys(obj)
18 | keys.sort()
19 |
20 | let params = []
21 |
22 | keys.forEach(e => {
23 | if (obj[e] != null) {
24 | params.push(e + '=' + obj[e])
25 | }
26 | })
27 |
28 | params.push('key=' + secret)
29 |
30 | let paramStr = params.join('&')
31 |
32 | const md5Util = require('../utils/md5.js')
33 | let signResult = md5Util.md5(paramStr).toUpperCase()
34 |
35 | return signResult
36 | }
37 |
38 |
39 | const getOrderParams = () => {
40 | /*
41 | * 用于获取小程序支付的参数
42 | *
43 | * 本函数仅用于【模拟后端】,在生产环境下订单号的生成、签名等过程应当在后端完成,
44 | * 由后端直接包装好 orderParams 返回并传递给小程序
45 | *
46 | */
47 |
48 | // 支付参数
49 | const totalFee = 100 // 支付金额,单位为分
50 | const body = '小程序支付测试' // 订单标题
51 | let nonce = getRandomNumber() // 随机数
52 | let timestamp = Date.now()
53 | let outTradeNo = 'TEST-WXA-' + timestamp + '-' + nonce // 商户端订单号
54 | let notifyUrl = null // 异步通知地址
55 | let attach = null // 异步通知附加数据
56 |
57 | let paramsObject = {
58 | mchid,
59 | total_fee: totalFee,
60 | out_trade_no: outTradeNo,
61 | body,
62 | attach,
63 | notify_url: notifyUrl,
64 | nonce
65 | }
66 |
67 | let sign = getSign(paramsObject)
68 |
69 | paramsObject.sign = sign
70 |
71 | return paramsObject
72 | }
73 |
74 | module.exports = {
75 | getOrderParams
76 | }
77 |
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | const utils = require('../../utils/util.js')
2 |
3 | Page({
4 | data: {
5 | orderParams: {}, // 支付发起参数
6 | orderResults: {}, // 支付结果
7 | preparePay: false, // 用户点击了支付按钮(订单信息交由 payjs 组件)
8 | paying: false, // 标记用户是否已经点击了「支付」并成功跳转到 PAYJS 小程序,该参数由 payjs 组件维护,用户可监听以在 onShow 生命周期函数中判断
9 | needRefreshOrderParams: false // 需要刷新订单信息,在此示例中,为了测试方便,允许支付一个订单号再进行一次新的支付行为;在生产环境下判断支付成功后直接跳转到订单页面即可
10 | },
11 | onTapPay: function() {
12 | // 事件处理函数 - 点击支付按钮
13 | this.setData({ preparePay: true })
14 | },
15 | onTapRefresh: function() {
16 | let orderParams = utils.getOrderParams()
17 | this.setData({
18 | orderParams,
19 | needRefreshOrderParams: false
20 | })
21 | },
22 | onLoad: function () {
23 | },
24 | onShow: function () {
25 | // 若处于支付中状态则交由 payjs 支付组件处理
26 | if (this.data.paying) return;
27 |
28 | // 尚未支付
29 | // 获取、设置支付参数
30 | let orderParams = utils.getOrderParams()
31 | this.setData({ orderParams })
32 | },
33 |
34 | /**
35 | * 支付成功的事件处理函数
36 | *
37 | * res.detail 为 payjs 小程序返回的订单信息
38 | *
39 | * 可通过 res.detail.payjsOrderId 拿到 payjs 订单号
40 | * 可通过 res.detail.responseData 拿到详细支付信息
41 | */
42 | bindPaySuccess (res) {
43 | console.log('success', res)
44 | console.log('[支付成功] PAYJS 订单号:', res.detail.payjsOrderId)
45 | this.setData({
46 | orderResults: res.detail,
47 | needRefreshOrderParams: true
48 | })
49 | wx.showModal({
50 | title: '支付成功',
51 | content: 'PAYJS 订单号:' + res.detail.payjsOrderId,
52 | showCancel: false
53 | })
54 | },
55 | /**
56 | * 支付失败的事件处理函数
57 | *
58 | * res.detail.error 为 true 代表传入小组件的参数存在问题
59 | * res.detail.navigateSuccess 代表了是否成功跳转到 PAYJS 小程序
60 | * res.detail.info 可能存有失败的原因
61 | *
62 | * 如果下单成功但是用户取消支付则可在 res.detail.info.payjsOrderId 拿到 payjs 订单号
63 | */
64 | bindPayFail (res) {
65 | console.log('fail', res)
66 | if (res.detail.error) {
67 | console.error('发起支付失败', res.detail.info)
68 | } else if (res.detail.navigateSuccess) {
69 | console.log('[取消支付] PAYJS 订单号:', res.detail.info.payjsOrderId)
70 | }
71 | },
72 | /**
73 | * 支付完毕的事件处理函数
74 | *
75 | * 无论支付成功或失败均会执行
76 | * 应当在此销毁 payjs 组件
77 | */
78 | bindPayComplete () {
79 | console.log('complete')
80 | this.setData({
81 | preparePay: false, // 销毁 PAYJS 组件
82 | })
83 | },
84 | /**
85 | * 组件内部数据被修改时的事件
86 | *
87 | * 当前仅用于监听 paying 数据
88 | * 当用户跳转到 PAYJS 小程序并等待返回的过程中 paying 值为 true
89 | */
90 | bindDataChange (res) {
91 | if (res.detail.paying) {
92 | this.setData({
93 | paying: res.detail.paying
94 | })
95 | }
96 | }
97 | })
98 |
--------------------------------------------------------------------------------
/components/payjs/payjs.js:
--------------------------------------------------------------------------------
1 | // componenets/payjs/payjs.js
2 | Component({
3 | /**
4 | * 组件的属性列表
5 | */
6 | properties: {
7 | params: { // 支付订单参数
8 | type: Object,
9 | value: null
10 | },
11 | envVersion: {
12 | type: String,
13 | value: "release"
14 | }
15 | },
16 |
17 | /**
18 | * 组件的初始数据
19 | */
20 | data: {
21 | showPayModal: false,
22 | paying: false
23 | },
24 |
25 | /**
26 | * 组件的方法列表
27 | */
28 | methods: {
29 | setPaying(newPayingData) {
30 | this.setData({
31 | paying: newPayingData
32 | })
33 | this.triggerEvent('dataChange', { paying: newPayingData })
34 | },
35 | onTapCancel () {
36 | // 用户点击了支付组件外的地方(灰色地方)
37 | console.log('[PAYJS] 跳转到 PAYJS 小程序失败 - 用户点击了提醒窗体以外的地方')
38 | this.triggerEvent('fail', { navigateSuccess: false })
39 | this.triggerEvent('complete')
40 | },
41 | navigateSuccess () {
42 | console.log('[PAYJS] 跳转到 PAYJS 小程序成功')
43 | // 成功跳转:标记支付中状态
44 | this.setPaying(true)
45 | },
46 | navigateFail (e) {
47 | // 跳转失败
48 | console.log('[PAYJS] 跳转到 PAYJS 小程序失败 - 失败回调', e)
49 | this.triggerEvent('fail', { navigateSuccess: false, info: e })
50 | this.triggerEvent('complete')
51 | }
52 | },
53 |
54 | /**
55 | * 组件生命周期
56 | */
57 | lifetimes: {
58 | attached() {
59 | this.setPaying(false)
60 | if (!this.data.params) {
61 | console.error('[PAYJS] 跳转到 PAYJS 小程序失败 - 错误:没有传递跳转参数', r)
62 | this.triggerEvent('fail', { error: true, navigateSuccess: false })
63 | this.triggerEvent('complete')
64 | }
65 |
66 | // 监听 app.onShow 事件
67 | wx.onAppShow(appOptions => {
68 | if (!this.data.paying) return;
69 |
70 | // 恢复支付前状态
71 | this.setPaying(false)
72 |
73 | if (appOptions.scene === 1038 && appOptions.referrerInfo.appId === 'wx959c8c1fb2d877b5') {
74 | // 来源于 PAYJS 小程序返回
75 | console.log('[PAYJS] 确认来源于 PAYJS 回调返回')
76 | let extraData = appOptions.referrerInfo.extraData
77 | if (extraData.success) {
78 | this.triggerEvent('success', extraData)
79 | this.triggerEvent('complete')
80 | } else {
81 | this.triggerEvent('fail', { navigateSuccess: true, info: extraData })
82 | this.triggerEvent('complete')
83 | }
84 | }
85 | })
86 |
87 | // 尝试直接跳转到 PAYJS 发起小程序支付
88 | wx.navigateToMiniProgram({
89 | appId: 'wx959c8c1fb2d877b5',
90 | path: 'pages/pay',
91 | extraData: this.data.params,
92 | envVersion: this.data.envVersion,
93 | success: r => {
94 | console.log('[PAYJS] 跳转到 PAYJS 小程序成功', r)
95 | // 成功跳转:标记支付中状态
96 | this.setPaying(true)
97 | },
98 | fail: e => {
99 | // 跳转失败:弹出提示组件引导用户跳转
100 | console.log('[PAYJS] 跳转到 PAYJS 小程序失败 - 准备弹窗提醒跳转', e)
101 | this.setData({
102 | showPayModal: true
103 | })
104 | }
105 | })
106 | }
107 | }
108 | })
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PAYJS 小程序支付框架与示例
2 |
3 | 本示例采用了框架 + 示例调用的形式,用于使用 [PAYJS](https://payjs.cn/ref/WDQGQD) 的小程序支付
4 |
5 |
6 | 注:根据微信规范,iOS 系统上不能使用虚拟支付
7 | 注:最低基础库为 2.4.0,请注意修改小程序后台设置中的最低基础库版本和开发者工具中的调试基础库版本
8 |
9 | ## 测试
10 | 克隆本仓库后使用微信开发者工具加载本项目源码,注意在创建项目时将 appid 修改为自己的
11 |
12 | 在 utils/util.js 中
13 |
14 | ```js
15 | // 在下面设置商户号
16 | const mchid = ''
17 |
18 | // 在下面设置密钥
19 | // 特别注意:此项设置应该仅供测试,生产环境下请在后端完成签名,切忌在小程序内暴露商户密钥
20 | const secret = ''
21 | ```
22 |
23 | 这两个部分分别设置为自己的商户号和密钥(不设置的话你测试用的钱结算到我这里我不还)
24 |
25 | ## 生产环境
26 |
27 | ### Step 1
28 |
29 | 在 app.json 文件中加入
30 |
31 | ```json
32 | "navigateToMiniProgramAppIdList": [
33 | "wx959c8c1fb2d877b5"
34 | ]
35 | ```
36 |
37 | ### Step 2
38 |
39 | 将 `components/payjs/*` 整个文件夹复制到你的项目中的相同结构文件夹
40 |
41 | ### Step 3
42 |
43 | 在需要用到支付功能的页面的 `.json` 文件中加入
44 |
45 | ```json
46 | "usingComponents": {
47 | "payjs": "/components/payjs/payjs"
48 | }
49 | ```
50 |
51 | ### Step 4
52 |
53 | 在需要用到支付功能的页面的 `.wxml` 文件**最底端**加入
54 |
55 | ```xml
56 |
57 | ```
58 |
59 | ### Step 5
60 |
61 | 在需要用到支付功能的页面的 `.js` 文件中的 `data` 数据内加入以下三个数据
62 |
63 | ```js
64 | {
65 | orderParams: {}, // 支付发起参数
66 | preparePay: false, // 控制 payjs 组件的创建与销毁
67 | paying: false, // 可选:如需知晓用户是否「已经跳转到了 PAYJS 小程序还未返回」的状态则可通过事件处理函数监听事件内的 paying 数据
68 | }
69 | ```
70 |
71 | 数据名称可以更改,但是更改时请注意保证和上面的 `.wxml` 文件内数据绑定及下面的事件处理函数中保持一致
72 |
73 |
74 | orderParams 用于放置发起支付的参数(必填)
75 | preparePay 用于控制 payjs 支付组件的创建与销毁
76 |
77 |
78 | 在此页面加入四个事件处理函数
79 |
80 | ```js
81 | /**
82 | * 支付成功的事件处理函数
83 | *
84 | * res.detail 为 PAYJS 小程序返回的订单信息
85 | *
86 | * 可通过 res.detail.payjsOrderId 拿到 PAYJS 订单号
87 | * 可通过 res.detail.responseData 拿到详细支付信息
88 | */
89 | bindPaySuccess (res) {
90 | console.log('success', res)
91 | console.log('[支付成功] PAYJS 订单号:', res.detail.payjsOrderId)
92 | this.setData({
93 | orderResults: res.detail,
94 | needRefreshOrderParams: true
95 | })
96 | wx.showModal({
97 | title: '支付成功',
98 | content: 'PAYJS 订单号:' + res.detail.payjsOrderId,
99 | showCancel: false
100 | })
101 | },
102 | /**
103 | * 支付失败的事件处理函数
104 | *
105 | * res.detail.error 为 true 代表传入小组件的参数存在问题
106 | * res.detail.navigateSuccess 代表了是否成功跳转到 PAYJS 小程序
107 | * res.detail.info 可能存有失败的原因
108 | *
109 | * 如果下单成功但是用户取消支付则可在 res.detail.info.payjsOrderId 拿到 payjs 订单号
110 | */
111 | bindPayFail (res) {
112 | console.log('fail', res)
113 | if (res.detail.error) {
114 | console.error('发起支付失败', res.detail.info)
115 | } else if (res.detail.navigateSuccess) {
116 | console.log('[取消支付] PAYJS 订单号:', res.detail.info.payjsOrderId)
117 | }
118 | },
119 | /**
120 | * 支付完毕的事件处理函数
121 | *
122 | * 无论支付成功或失败均会执行
123 | * 应当在此销毁 payjs 组件
124 | */
125 | bindPayComplete () {
126 | console.log('complete')
127 | this.setData({
128 | preparePay: false, // 销毁 payjs 组件
129 | })
130 | },
131 | /**
132 | * 【可选】组件内部数据被修改时的事件
133 | *
134 | * 当前仅用于监听 paying 数据
135 | * 当用户跳转到 PAYJS 小程序并等待返回的过程中 paying 值为 true
136 | */
137 | bindDataChange (res) {
138 | if (res.detail.paying) {
139 | this.setData({
140 | paying: res.detail.paying
141 | })
142 | }
143 | }
144 |
145 | ```
146 |
147 | ### Step 6
148 |
149 | 为支付页面的支付按钮绑定以下事件
150 |
151 | ```xml
152 |
153 | ```
154 |
155 | ```js
156 | onTapPay: function() {
157 | // 事件处理函数 - 点击支付按钮
158 | this.setData({
159 | preparePay: true,
160 | orderParams: {
161 | // 这里传入【后端返回并签名完毕】的支付发起参数
162 | }
163 | })
164 | },
165 | ```
166 |
167 | ## 写在最后 & 免责声明
168 |
169 | 1. 订单信息的生成、签名与订单支付状态的判断都应该在后端返回和判断,小程序的返回值仅应作为参考使用,应以为准
170 | 2. 如在使用过程中有问题,请阅读代码,如仍有问题,请发 Issue
171 | 3. 本人不保证此框架可一直可用也不为此框架所产生的任何问题负责
172 |
--------------------------------------------------------------------------------
/utils/md5.js:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | *
4 |
5 | * MD5 (Message-Digest Algorithm)
6 |
7 | * http://www.webtoolkit.info/
8 |
9 | *
10 |
11 | **/
12 |
13 |
14 |
15 |
16 | var MD5 = function (string) {
17 |
18 |
19 |
20 |
21 | function RotateLeft(lValue, iShiftBits) {
22 |
23 |
24 | return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
25 |
26 |
27 | }
28 |
29 |
30 |
31 |
32 | function AddUnsigned(lX, lY) {
33 |
34 |
35 | var lX4, lY4, lX8, lY8, lResult;
36 |
37 |
38 | lX8 = (lX & 0x80000000);
39 |
40 |
41 | lY8 = (lY & 0x80000000);
42 |
43 |
44 | lX4 = (lX & 0x40000000);
45 |
46 |
47 | lY4 = (lY & 0x40000000);
48 |
49 |
50 | lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
51 |
52 |
53 | if (lX4 & lY4) {
54 |
55 |
56 | return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
57 |
58 |
59 | }
60 |
61 |
62 | if (lX4 | lY4) {
63 |
64 |
65 | if (lResult & 0x40000000) {
66 |
67 |
68 | return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
69 |
70 |
71 | } else {
72 |
73 |
74 | return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
75 |
76 |
77 | }
78 |
79 |
80 | } else {
81 |
82 |
83 | return (lResult ^ lX8 ^ lY8);
84 |
85 |
86 | }
87 |
88 |
89 | }
90 |
91 |
92 |
93 |
94 | function F(x, y, z) { return (x & y) | ((~x) & z); }
95 |
96 |
97 | function G(x, y, z) { return (x & z) | (y & (~z)); }
98 |
99 |
100 | function H(x, y, z) { return (x ^ y ^ z); }
101 |
102 |
103 | function I(x, y, z) { return (y ^ (x | (~z))); }
104 |
105 |
106 |
107 |
108 | function FF(a, b, c, d, x, s, ac) {
109 |
110 |
111 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
112 |
113 |
114 | return AddUnsigned(RotateLeft(a, s), b);
115 |
116 |
117 | };
118 |
119 |
120 |
121 |
122 | function GG(a, b, c, d, x, s, ac) {
123 |
124 |
125 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
126 |
127 |
128 | return AddUnsigned(RotateLeft(a, s), b);
129 |
130 |
131 | };
132 |
133 |
134 |
135 |
136 | function HH(a, b, c, d, x, s, ac) {
137 |
138 |
139 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
140 |
141 |
142 | return AddUnsigned(RotateLeft(a, s), b);
143 |
144 |
145 | };
146 |
147 |
148 |
149 |
150 | function II(a, b, c, d, x, s, ac) {
151 |
152 |
153 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
154 |
155 |
156 | return AddUnsigned(RotateLeft(a, s), b);
157 |
158 |
159 | };
160 |
161 |
162 |
163 |
164 | function ConvertToWordArray(string) {
165 |
166 |
167 | var lWordCount;
168 |
169 |
170 | var lMessageLength = string.length;
171 |
172 |
173 | var lNumberOfWords_temp1 = lMessageLength + 8;
174 |
175 |
176 | var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
177 |
178 |
179 | var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
180 |
181 |
182 | var lWordArray = Array(lNumberOfWords - 1);
183 |
184 |
185 | var lBytePosition = 0;
186 |
187 |
188 | var lByteCount = 0;
189 |
190 |
191 | while (lByteCount < lMessageLength) {
192 |
193 |
194 | lWordCount = (lByteCount - (lByteCount % 4)) / 4;
195 |
196 |
197 | lBytePosition = (lByteCount % 4) * 8;
198 |
199 |
200 | lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
201 |
202 |
203 | lByteCount++;
204 |
205 |
206 | }
207 |
208 |
209 | lWordCount = (lByteCount - (lByteCount % 4)) / 4;
210 |
211 |
212 | lBytePosition = (lByteCount % 4) * 8;
213 |
214 |
215 | lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
216 |
217 |
218 | lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
219 |
220 |
221 | lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
222 |
223 |
224 | return lWordArray;
225 |
226 |
227 | };
228 |
229 |
230 |
231 |
232 | function WordToHex(lValue) {
233 |
234 |
235 | var WordToHexValue = "", WordToHexValue_temp = "", lByte, lCount;
236 |
237 |
238 | for (lCount = 0; lCount <= 3; lCount++) {
239 |
240 |
241 | lByte = (lValue >>> (lCount * 8)) & 255;
242 |
243 |
244 | WordToHexValue_temp = "0" + lByte.toString(16);
245 |
246 |
247 | WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
248 |
249 |
250 | }
251 |
252 |
253 | return WordToHexValue;
254 |
255 |
256 | };
257 |
258 |
259 |
260 |
261 | function Utf8Encode(string) {
262 |
263 |
264 | string = string.replace(/\r\n/g, "\n");
265 |
266 |
267 | var utftext = "";
268 |
269 |
270 |
271 |
272 | for (var n = 0; n < string.length; n++) {
273 |
274 |
275 |
276 |
277 | var c = string.charCodeAt(n);
278 |
279 |
280 |
281 |
282 | if (c < 128) {
283 |
284 |
285 | utftext += String.fromCharCode(c);
286 |
287 |
288 | }
289 |
290 |
291 | else if ((c > 127) && (c < 2048)) {
292 |
293 |
294 | utftext += String.fromCharCode((c >> 6) | 192);
295 |
296 |
297 | utftext += String.fromCharCode((c & 63) | 128);
298 |
299 |
300 | }
301 |
302 |
303 | else {
304 |
305 |
306 | utftext += String.fromCharCode((c >> 12) | 224);
307 |
308 |
309 | utftext += String.fromCharCode(((c >> 6) & 63) | 128);
310 |
311 |
312 | utftext += String.fromCharCode((c & 63) | 128);
313 |
314 |
315 | }
316 |
317 |
318 |
319 |
320 | }
321 |
322 |
323 |
324 |
325 | return utftext;
326 |
327 |
328 | };
329 |
330 |
331 |
332 |
333 | var x = Array();
334 |
335 |
336 | var k, AA, BB, CC, DD, a, b, c, d;
337 |
338 |
339 | var S11 = 7, S12 = 12, S13 = 17, S14 = 22;
340 |
341 |
342 | var S21 = 5, S22 = 9, S23 = 14, S24 = 20;
343 |
344 |
345 | var S31 = 4, S32 = 11, S33 = 16, S34 = 23;
346 |
347 |
348 | var S41 = 6, S42 = 10, S43 = 15, S44 = 21;
349 |
350 |
351 |
352 |
353 | string = Utf8Encode(string);
354 |
355 |
356 |
357 |
358 | x = ConvertToWordArray(string);
359 |
360 |
361 |
362 |
363 | a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
364 |
365 |
366 |
367 |
368 | for (k = 0; k < x.length; k += 16) {
369 |
370 |
371 | AA = a; BB = b; CC = c; DD = d;
372 |
373 |
374 | a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
375 |
376 |
377 | d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
378 |
379 |
380 | c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
381 |
382 |
383 | b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
384 |
385 |
386 | a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
387 |
388 |
389 | d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
390 |
391 |
392 | c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
393 |
394 |
395 | b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
396 |
397 |
398 | a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
399 |
400 |
401 | d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
402 |
403 |
404 | c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
405 |
406 |
407 | b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
408 |
409 |
410 | a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
411 |
412 |
413 | d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
414 |
415 |
416 | c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
417 |
418 |
419 | b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
420 |
421 |
422 | a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
423 |
424 |
425 | d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
426 |
427 |
428 | c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
429 |
430 |
431 | b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
432 |
433 |
434 | a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
435 |
436 |
437 | d = GG(d, a, b, c, x[k + 10], S22, 0x2441453);
438 |
439 |
440 | c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
441 |
442 |
443 | b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
444 |
445 |
446 | a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
447 |
448 |
449 | d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
450 |
451 |
452 | c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
453 |
454 |
455 | b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
456 |
457 |
458 | a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
459 |
460 |
461 | d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
462 |
463 |
464 | c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
465 |
466 |
467 | b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
468 |
469 |
470 | a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
471 |
472 |
473 | d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
474 |
475 |
476 | c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
477 |
478 |
479 | b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
480 |
481 |
482 | a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
483 |
484 |
485 | d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
486 |
487 |
488 | c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
489 |
490 |
491 | b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
492 |
493 |
494 | a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
495 |
496 |
497 | d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
498 |
499 |
500 | c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
501 |
502 |
503 | b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
504 |
505 |
506 | a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
507 |
508 |
509 | d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
510 |
511 |
512 | c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
513 |
514 |
515 | b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
516 |
517 |
518 | a = II(a, b, c, d, x[k + 0], S41, 0xF4292244);
519 |
520 |
521 | d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
522 |
523 |
524 | c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
525 |
526 |
527 | b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
528 |
529 |
530 | a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
531 |
532 |
533 | d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
534 |
535 |
536 | c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
537 |
538 |
539 | b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
540 |
541 |
542 | a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
543 |
544 |
545 | d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
546 |
547 |
548 | c = II(c, d, a, b, x[k + 6], S43, 0xA3014314);
549 |
550 |
551 | b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
552 |
553 |
554 | a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
555 |
556 |
557 | d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
558 |
559 |
560 | c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
561 |
562 |
563 | b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
564 |
565 |
566 | a = AddUnsigned(a, AA);
567 |
568 |
569 | b = AddUnsigned(b, BB);
570 |
571 |
572 | c = AddUnsigned(c, CC);
573 |
574 |
575 | d = AddUnsigned(d, DD);
576 |
577 |
578 | }
579 |
580 |
581 |
582 |
583 | var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d);
584 |
585 |
586 |
587 |
588 | return temp.toLowerCase();
589 |
590 | }
591 |
592 | module.exports = {
593 | MD5,
594 | md5: MD5
595 | }
596 |
597 |
--------------------------------------------------------------------------------