支付宝手机网站支付集成笔记

一、创建应用:

https://openhome.alipay.com/platform/developerIndex.htm开发者中心创建应用,然后签约手机网站支付,提交审核,一般需要一天时间审核。

二、配置应用信息:

如下图:

因为第三步已经上传了公钥,所以显示如上图。未设置情况显示“设置应用公钥”。

1.设置应用公钥步骤:

https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1页面根据自己系统下载windows版或者mac版的生成密钥工具,然后按照文档操作即可。

我选择的是PKCS8(JAVA适用),密钥长度选择2048,点击生成密钥后,将商户应用私钥复制到代码中使用,将公钥上传(也就是“设置应用公钥”点击后的文本框,在上图3的位置)。上传成功后如3所示,点击查看支付宝公钥,将公钥粘贴到代码中使用。

三、集成:

官方集成文档地址:https://doc.open.alipay.com/doc2/detail?treeId=203&articleId=105288&docType=1

demo:

if(request.getParameter("WIDout_trade_no")!=null){
    // 商户订单号,商户网站订单系统中唯一订单号,必填
    String out_trade_no = "自己设置的订单号";
    // 订单名称,必填
    String subject = "赛事赞助";
    // 付款金额,必填 填每笔订单的金额
    String total_amount=100+"";
    // 商品描述,可空
    String body = "钛度杯war3大师赛众筹奖金";
    // 超时时间 可空 支付的超时时间,单位是分
   String timeout_express="15m";
    // 销售产品码 必填
    String product_code="QUICK_WAP_PAY";
    /**********************/
    // SDK 公共请求类,包含公共请求参数,以及封装了签名与验签,开发者无需关注签名与验签     
    //调用RSA签名方式
    AlipayClient client = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
    AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();
    
    // 封装请求支付信息
    AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
    model.setOutTradeNo(out_trade_no);
    model.setSubject(subject);
    model.setTotalAmount(total_amount);
    model.setBody(body);
    model.setTimeoutExpress(timeout_express);
    model.setProductCode(product_code);
    alipay_request.setBizModel(model);
    // 设置异步通知地址
    alipay_request.setNotifyUrl(AlipayConfig.notify_url);
    // 设置同步地址
    alipay_request.setReturnUrl(AlipayConfig.return_url);   
    
    // form表单生产
    String form = "";
	try {
		// 调用SDK生成表单
		form = client.pageExecute(alipay_request).getBody();
		response.setContentType("text/html;charset=" + AlipayConfig.CHARSET); 
	    response.getWriter().write(form);//直接将完整的表单html输出到页面 
	    response.getWriter().flush(); 
	    response.getWriter().close();
	} catch (AlipayApiException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} 
}

 

关于自定义参数:

该字段可以加自定义参数,但是我用JSON格式字符串会报错,提示系统繁忙,请稍候再试,或者是支付超时,所以最后把参数用String组合了一下,注意必须用URLEncoder编码,代码如下:

String paramsTmp = uname + ":" + sid;
        String params = null;
        try {
            params = URLEncoder.encode(paramsTmp, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(AlipayService.class.getName()).log(Level.SEVERE, null, ex);
        }
        model.setPassbackParams(params);

然后支付宝异步通知会原样带回自定义参数,取参数方法:

String unameStrTmp = request.getParameter("passback_params");
String unameStr = URLDecoder.decode(unameStrTmp, "UTF-8");

不同于取其他参数的方法:

String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");

取到参数后就可以根据自己定义的参数类型进行处理了。

四、关于沙箱环境:

1.沙箱环境需要重新在沙箱环境配置应用环境,也就是步骤二。沙箱环境和正式环境的配置有以下五处不同:

//沙箱环境appid,可以在沙箱应用配置找到
public static String APPID = "2016080100144644";
//沙箱环境固定地址
public static String URL = "https://openapi.alipaydev.com/gateway.do";
//沙箱环境下的支付宝公钥(因为用了和正式环境同一套密钥,所以只需要改支付宝公钥即可,如果不是同一套还需要改私钥)
public static String ALIPAY_PUBLIC_KEY = "";
//支付宝通知地址,必须外网可以访问,该地址在沙箱环境下可以收到支付宝通知
public static String notify_url_web = "http://101.201.76.66:8080/api/alipay/mobilenotify";
public static String return_url_web = "http://101.201.76.66:8080/app/fund/paycallback";

 

2.沙箱应用配置、沙箱账号、沙箱工具都可以在开发者中心的如下页面找到。

 

3.沙箱环境配置好后,需要下载沙箱工具进行测试,也就是一个测试环境下的支付宝app,就是在上图的沙箱工具中进行下载(只有安卓版,没有IOS版),账号在上图沙箱账号中获取,里面有很多钱可以进行测试。

 

附:支付宝官方沙箱环境使用说明:https://doc.open.alipay.com/doc2/detail.htm?treeId=200&articleId=105311&docType=1

五、测试通过后,记得改回正式环境,然后就可以上线了。

尤其注意集成时如果需要带自定义参数,可能会有问题,当时就是自定义参数坑了半天,同时还要考虑编码。

ps:因为是后端直接返回了form表单,所以前端必须在当前网页打开,不能另起网页,另起网页好像会有问题,就是点下订单了没反应。

Lua学习笔记

一、Lua安装:

1.  Lua官网下载安装,“Lua for Windows”下载地址如下:

http://luaforwindows.luaforge.net/  (需要翻墙)

ps:安装Lua时,需要预习安装VC2005运行时库,否则安装时会出错,Microsoft Visual C++ 2005 SP1下载地址:https://www.microsoft.com/en-us/download/confirmation.aspx?id=5638

2.安装之后就可以打开SciTE,输入lua代码:

print("hello,lua")

然后保存文件(注意后缀名必须为.lua),然后按F5运行程序,可以看到输出窗口如下显示:

则代表整个Lua开发环境安装成功。

ps:也可以直接打开Lua快捷方式,在里面编辑程序。

Redis学习笔记

简介

Remote Dictionary Server (Redis) ,Redis是一个开源的高性能键值对数据库。

ps:Redis官网没有windows版本,需要去github下载。

下载地址:https://github.com/dmajkic/redis/downloads。或者https://github.com/MicrosoftArchive/redis/releases

一、启动:

打开一个cmd窗口 使用cd命令切换目录到安装目录运行 redis-server.exe redis.windows.conf

这时候另启一个cmd窗口,原来的不要关闭,不然就无法访问服务端了。

切换到redis目录下运行 redis-cli.exe -h 127.0.0.1 -p 6379 ,或者直接运行redis-cli

Redis选择数据库命令:select 0 选择到0数据库( redis默认的数据库是0~15一共16个数据库)

注:

redis可视化工具下载地址:https://redisdesktop.com/download

二、Redis数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

String(字符串):

string是redis最基本的类型,一个key对应一个value。

使用set命令设置key和value,使用get得到value。

set    key   value

get    key

注意:一个键最大能存储512MB。

Hash(哈希):

Redis hash 是一个键值对集合。

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HEGTALL 命令,user:11915为键值。

每个 hash 可以存储 232 – 1 键值对(40多亿)。

List(列表):

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

然后再添加一个元素到左边:

列表最多可存储 232 – 1 元素 (4294967295, 每个列表可存储40多亿)。

Set(集合):

Redis的Set是string类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

sadd 命令

添加一个string元素到,key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key对应的set不存在返回错误。

命令:sadd key member

然后再添加一个相同value的元素:

注意:以上实例中 beijing添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。

集合中最大的成员数为 232 – 1 (4294967295, 每个集合可存储40多亿个成员)。

zset(sorted set:有序集合):

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

zadd 命令

添加元素到集合,元素在集合中存在则更新对应score

命令:zadd key score member

三、Redis事务

一般情况下redis在接受到一个client发来的命令后会立即处理并 返回处理结果,但是当一个client在一个连接中发出multi命令有,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一 个队列中。当从此连接受到exec命令后,redis会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给client.然后此连接就 结束事务上下文。

四、Redis脚本

eval命令:

EVAL script numkeys key [key ...] arg [arg ...]

以上为eval的语法格式,其中:

<1> script:     自己的lua脚本

<2> numkeys:  key的个数

<3> key:         redis中各种数据结构的替代符号

<4> arg:         你的自定义参数

注意: numkeys是key的个数,KEYS和ARGV必须要大写。

 

使用Redis和lua脚本文件:

lua脚本文件内容:

return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}

然后cmd,cd到Redis的安装目录,执行命令:

可以发现成功调用了lua脚本。

重要:其中的参数个数2可以省略,但是一定要有逗号,且逗号前后必须要有空格。否则,会造成lua脚本ARGV为nil的问题。

特别注意:不能使用Redis客户端执行以上操作,如果用Redis客户端的eval命令,则可能会报以下错误:

(error) ERR Error compiling script (new function): user_script:1: ‘<name>’ expected near ‘\’

或者:

(error) ERR value is not an integer or out of range

 

五、Redis远程连接:

cd到本地redis目录,然后执行:redis-cli -h  IP地址 -p 6379 -a 密码

Cloneable接口

基本类型:存放在栈空间中。
引用类型:存放在堆空间中。

 

如果类没有实现Cloneable接口,调用类对象的clone方法会抛出CloneNotSupportedException,Object提供的clone方法是浅度复制的。

Demo:

public class TestCloneable {

    public static void main(String[] args) throws CloneNotSupportedException {
        Name name = new Name("jack");
        Person person = new Person(name, 20);
        Person clonePerson = (Person) person.clone();
        name.setFirstName("john");
        person.setAge(18);
        System.out.println("name is:" + clonePerson.getName().getFirstName() + ",age is:" + clonePerson.getAge());
    }

}

class Name {

    private String firstName;

    public Name(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

class Person implements Cloneable {

    private Name name;
    private int age;

    public Person(Name name, int age) {
        this.name = name;
        this.age = age;
    }

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

输出结果:name is: john, age is: 20

因为clone后,clonePerson的name是引用类型,所以当原name的值变化后,clonePerson的值也会变,age是基本类型,clone后不变。(引用类似于指针,变化后指向堆中不同的地方。)