Posts in category “Programming”

Superpay 小程序支付后端接口对接记

这两天在和开发小程序的同事对接supay支付,今天总算基本搞完了。他们的文档过于糟糕,太多槽要吐,记录一下也许会帮到其他也需要对接superpay的人。先说说我们的业务流程

  1. 小程序 wx.login 拿到 code
  2. 小程序 wx.getUserInfo 拿到加密的 userData和iv
  3. 小程序登录请求公司后端服务器 /wechatMiniProgram/login 接口三方登录,登录成功拿到可访问公司其他接口的 accessToken
  4. 小程序拿着accessToken下订单
  5. 小程序拿着订单id请求公司后端服务器 /supay/wechatMiniProgramPay/{orderId}
  6. 公司服务器请求 supay 小程序下单接口,获取pay_info 给小程序。故事主要发生在这里。

他们的文档里是有一个流程图的,写成文本大体是下面这样。

  1. 小程序向公司后台服务器发起下单支付请求
  2. 公司后台服务器请求 superpay 服务器获取 openid
  3. superpay 服务器返回 openid 给公司后台服务器
  4. 公司后台服务器拿着openid 请求superpay服务器(小程序下单接口)拿 pay_info
  5. 公司服务器把 pay_info 返回给小程序
  6. 小程序拿 pay_info 发起支付

这个流程图事后看其实也不算错,只是其中第2步和第3步是不必要的。因为小程序在三方登录的时候,我们已经可以拿到openid和unionid。补充一句,小程序支付只需要openid,并不需要unionid。

在和superpay的技术同事沟通过程中,我发现最有价值的聊天记录包括

不要管其他的接口了,就用小程序下单
交易状态 TRADE_FINISHED 不需要处理
小程序下单返回的接口数据中,所有时间均是北京时间
inner_trade_no是 supay 的单号,mch_order_no和outer_trade_no 是你们的单号
trade_amount是人民币金额,虽然例子里写的是纽币
签名的时候字典序是重要的,文档里虽然没有提到,但在签名串字典序末尾追加一个 &key=商户appsecret 再md5是至关重要的。

因为签名需要拼一个query string格式的串,我就想当然的认为是post这个串到supay服务器。这是我的错,这里要以例子为准。例子里写的就是post一个json格式的数据过去。

10.07.2020 补充一个吐槽,小程序返回的 trade_amount 单位并不是人民币,是纽币。而且小程序各接口没有一个能返回人民币实际支付额。我的解决办法是拿自己的订单号调用 superpay 给非小程序支付的查询交易详情接口,这样就能拿到人民币交易金额数据。

JavaScript中七种函数调用方式及对应 this 的含义

旧文重发存档,原文发表于9年前。原文地址

this 在 JavaScript 开发中占有相当重要的地位,不过很多人对this这个东西都感觉到琢磨不透。要真正理解JavaScript的函数机制,就非常有必要搞清楚this到底是怎么回事。

函数调用方式不同,this 含义也跟着不同。JavaScript语言中有七种调用函数方式:

第一种:调用方法

var obj = {
    method: function() { alert(this === obj); }
}
obj.method();

上面这行obj.method()显然method是作为方法被调用,这种情况下,函数体中的this绑定的就是method的宿主对象,也就是obj。 从这种调用方式我们得出第一定律: 第一定律:以方法方式调用函数,this则绑定宿主对象。

第二种:调用全局函数

var method = function(){alert(this === window);}
method();

上面这个函数是个全局函数。我们知道,全局变量或函数都相当于window对象的属性。也就是说,上面这句实际上等同于下面这句:

window.method = function(){alert(this === window);}
window.method();

既然这样,那么这里 this 绑定到 window 对象就显而易见,很容易理解了。相当于方法调用模式的一个特例,并不违背第一定律。

第三种:全局函数内调用内部函数

var m_ext = function() {
    alert(this === window);
    var m_inner = function() {
       alert(this === window);
    }
   m_inner();
}
m_ext();

执行上面这段代码,你会惊讶的发现,两个布尔表达式的值都是真。也就是说子函数孙函数,只要是以函数的方式调用,this 就铁了心绑定 window 对象了。从这种调用方式我们得出第二定律: 第二定律:不论子函数孙函数,只要是以函数的方式调用,this 就铁了心绑定 window 对象。

第四种:方法内调用内部函数

var obj = {};
obj.method = function() {
    alert(this === obj);
    var m_inner = function() {
       alert(this === window);
    }
   m_inner();
}
obj.method();

执行上面这段代码,第一个this绑定到obj你不奇怪,第二个this绑定到window其实也不奇怪。它仍然遵守第二定律,也就是不论子函数孙函数,只要是以函数的方式调用,this 就铁了心绑定 window 对象。

第五种:作为构造函数调用

function Person(name, age){
    this.name = name;
    this.age = age;
}
var john = new Person('John', 38);
alert(JSON.stringify(john));

你会说,哇,这很眼熟。生成某个类的新对象啊。Javascript就是这么另类,函数确实可以这么调。 这次弹出的是这样一个字符串:{"name":"John","age":38}。哇,我明明没在函数定义里写return 语句,它却主动返回了一个对象给我。没错,即便你在函数定义里return某个东西,它也不会理你。(注意:如果你return一个function就会有惊喜,不信你试试看)。从这种调用方式我们得出第三定律:

第三定律:如果在一个函数本身没有返回值,在其前面加上 new 调用,就会创建一个连接到该函数 prototype 属性的新对象,再把 this 绑定到该对象,然后执行该函数,最后返回该对象。如果该函数有返回值,且返回值为字符串/数字/布尔值等简单对象的话,该返回值会被丢弃。但如果该函数的返回值为对象,函数或者数组等复杂对象的话,该函数则会返回该返回值,抛弃this绑定的对象。据我测试,如果返回值是一个函数时,该函数会返回undefined。我暂时还不知道为什么会这样。

第六种:用函数对象的apply方法调用

var my_concat = function(stra, strb){
   alert(this);
    return stra + '' + strb;
}
var param = ['hello', ' world']
alert(my_concat.apply('bullshit', param));

你一定注意了,在这里 this 绑定的是apply方法的第一个参数 'bullshit'。从这里我们得出第四定律: 第四定律:用方法的apply/call方法调用方法时,this 则被绑定到apply/call方法的第一个参数。 想谁就是谁,嗯,吴妈也行。JavaScript的程序员们好幸福哦。

第七种:用函数对象的call方法调用

var my_concat = function(stra, strb){
   alert(this);
    return stra + '' + strb;
}
alert(my_concat.call('bullshit', 'hello',' world'));

对啦,你或许会多问一句,apply 的第二个参数有什么规定?call方法和apply又有什么不同?嗯 ,真是勤学好问,我就再啰嗦一下说说apply和call:

apply 和 call 的区别:

apply要求第二个参数必须是数组。否则就会报 TypeError: second argument to Function.prototype.apply must be an array. 而call则没这么严格啦,第二个参数要什么类型?随意啦,还可以有第三个第四个第五个第六个....啦。

我想我说的够清楚了。感谢《JavaScript The Good Parts》和《JavaScript Definitive Guide》,要不然我也弄不明白呢!

A quick way to run a database update periodically by crontab

  1. put a .my.cnf file into your home directory, for example
$ cat ~/.my.cnf 
[client]
user=yourDbUserName
password=yourDbPassword
host=yourDbHost
  1. write a bash script to update the database
#!/bin/bash

set -ex

variable=`TZ="Africa/Abidjan" date '+%Y-%m-%d %H:%M:%S'`
sql="Your SQL statement with ${variable}"

logDir=/data/Logs/`date '+%Y'`/`date '+%m'`
mkdir -p "${logDir}"
echo "${sql}" >> "${logDir}/`date '+%d'`.sql"
mysql -v -v -e "${sql}" >> "${logDir}/`date '+%d'`.result.txt"
  1. put a line by run crontab -e

the end.

vue-cli-service Modes vs Environment Variables

严格来说,二者是两个维度,并没有必然的固定的映射关系。只是通常情况下,约定俗成的:

--mode test <=> NODE_ENV=test
--mode development <=> NODE_ENV=development
--mode production <=>  NODE_ENV=production
--mode staging <=> NODE_ENV=staging

那怎么知道手头的项目是否符合这个约定俗成呢?通常来说,你可以看一下项目根目录下的 .env 系列文件,一般情况下,NODE_ENV 变量会定义在那里。

Two silly moments when I develop using Vue.js

  1. Bound object changes but the UI didn't change. Solution:

Template part,

<el-select v-model="form.categoryIdList" multiple @change="setValue(form.categoryIdList)">

Component part,

    setValue(object) {
      this.$set(this.form, this.form.categoryIdList, object)
    },
  1. VUE_APP_CUSTOM_VARIBLE doesn't work, solution:

Restart your dev server.