├── 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 | --------------------------------------------------------------------------------