最近项目需要接入支付功能 为了给安卓和ios搞支付接口也是跑了很多弯路,现在记录下来支付宝支付的一些操作过程,方便以后再遇到。
在支付宝demo中下载了所需的jar包:
① alipay-sdk-java20170725114550.jar
② alipay-sdk-java20170725114550-source.jar 源代码
③ commons-logging-1.1.1.jar
④ commons-logging-1.1.1-sources.jar
接下里是代码:代码都是网上找的,复制粘贴就可以用了
public class AlipayConfig {
// 合作身份者ID,以2088开头由16位纯数字组成的字符串
public static String partner = "";
// 商户的私钥,使用支付宝自带的openssl工具生成。
public static String private_key="";
// 支付宝的公钥,无需修改该值
public static String ali_public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRAFljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQEB/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5KsiNG9zpgmLCUYuLkxpLQIDAQAB";
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static String seller = "";
// 调试用,创建TXT日志文件夹路径
public static String log_path = "d:\\";
// 字符编码格式 目前支持 gbk 或 utf-8
public static String input_charset = "UTF-8";
// 签名方式 不需修改
public static String sign_type = "RSA";
// 接收通知的接口名(这里的域名或者ip需要填写外网可以访问的地址)
public static String service = "http://127.0.0.1:8080/AppServer/CallbackServlet";
//APPID
public static String app_id="";
}
上面配置中partner是申请支付宝商户时支付宝生成的一个商户号登陆支付宝商户账号密码访问:https://openhome.alipay.com/platform/keyManage.htm?keyType=partner
私钥:private_key这个在支付宝的文档里有生成的方式,基于java后台使用,需要将这个密钥转成PKCS8,
在蚂蚁金服的开放平台设置和上传公钥,私钥转成pkcs8格式放到代码中
主要配置的地方
appid是在开放平台新建应用的appid
service这个需要注意一下,这个是处理最终订单完成时修改数据库标记所需要的一个链接,这个链接需要外网可访问的地址,不能写一个127.0.0.1这样的本地地址
配置完成后就是主要的业务了
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.PartSource;
public class AlipayCore {
/**
* 除去数组中的空值和签名参数
*
* @param sArray
* 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<String, String>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("")
|| key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param params
* 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
public static String sign(String content, String privateKey) {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance(AlipayConfig.sign_type);
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature.getInstance(AlipayConfig.SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update(content.getBytes(AlipayConfig.input_charset));
byte[] signed = signature.sign();
return Base64.encode(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
*
* @param sWord
* 要写入日志里的文本内容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(AlipayConfig.log_path + "alipay_log_"
+ System.currentTimeMillis() + ".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 生成文件摘要
*
* @param strFilePath
* 文件路径
* @param file_digest_type
* 摘要算法
* @return 文件摘要结果
*/
public static String getAbstract(String strFilePath, String file_digest_type)
throws IOException {
PartSource file = new FilePartSource(new File(strFilePath));
if (file_digest_type.equals("MD5")) {
return DigestUtils.md5Hex(file.createInputStream());
} else if (file_digest_type.equals("SHA")) {
return DigestUtils.sha256Hex(file.createInputStream());
} else {
return "";
}
}
/**
* create the order info. 创建订单信息
*
*/
public static String getOrderInfo(String subject, String body, String price) {
// 合作者身份ID
String orderInfo = "partner=" + "\"" + AlipayConfig.partner + "\"";
// 卖家支付宝账号
orderInfo += "&seller_id=" + "\"" + AlipayConfig.seller + "\"";
// 商户网站唯一订单号
orderInfo += "&out_trade_no=" + "\"" + UtilDate.getOrderNum() + "\"";
// 商品名称
orderInfo += "&subject=" + "\"" + subject + "\"";
// 商品详情
orderInfo += "&body=" + "\"" + body + "\"";
// 商品金额
orderInfo += "&total_fee=" + "\"" + price + "\"";
// 服务器异步通知页面路径
orderInfo += "¬ify_url=" + "\""
+ "http://http://10.60.7.92:8080/AppServer/CallbackServlet" + "\"";
// 接口名称, 固定值
orderInfo += "&service=\"mobile.securitypay.pay\"";
// 支付类型, 固定值
orderInfo += "&payment_type=\"1\"";
// 参数编码, 固定值
orderInfo += "&_input_charset=\"utf-8\"";
// 设置未付款交易的超时时间
// 默认30分钟,一旦超时,该笔交易就会自动被关闭。
// 取值范围:1m~15d。
// m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。
// 该参数数值不接受小数点,如1.5h,可转换为90m。
orderInfo += "&it_b_pay=\"1d\"";
// 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空
//orderInfo += "&return_url=\"http://www.tianjiandao.com\"";
// 调用银行卡支付,需配置此参数,参与签名, 固定值
// orderInfo += "&paymethod=\"expressGateway\"";
return orderInfo;
}
public static String getSignType() {
return "sign_type=\"RSA\"";
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
public class AlipayNotify {
/**
* 支付宝消息验证地址
*/
private static final String HTTPS_VERIFY_URL = "https://mapi.alipay.com/gateway.do?service=notify_verify&";
/**
* 验证消息是否是支付宝发出的合法消息
* @param params 通知返回来的参数数组
* @return 验证结果
*/
public static boolean verify(Map<String, String> params) {
//判断responsetTxt是否为true,isSign是否为true
//responsetTxt的结果不是true,与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
//isSign不是true,与安全校验码、请求时的参数格式(如:带自定义参数等)、编码格式有关
String responseTxt = "true";
if(params.get("notify_id") != null) {
String notify_id = params.get("notify_id");
responseTxt = verifyResponse(notify_id);
}
String sign = "";
if(params.get("sign") != null) {sign = params.get("sign");}
boolean isSign = getSignVeryfy(params, sign);
//写日志记录(若要调试,请取消下面两行注释)
//String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数:" + AlipayCore.createLinkString(params);
//AlipayCore.logResult(sWord);
if (isSign && responseTxt.equals("true")) {
return true;
} else {
return false;
}
}
/**
* 根据反馈回来的信息,生成签名结果
* @param Params 通知返回来的参数数组
* @param sign 比对的签名结果
* @return 生成的签名结果
*/
private static boolean getSignVeryfy(Map<String, String> Params, String sign) {
//过滤空值、sign与sign_type参数
Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
//获取待签名字符串
String preSignStr = AlipayCore.createLinkString(sParaNew);
//获得签名验证结果
boolean isSign = false;
if(AlipayConfig.sign_type.equals("RSA")){
isSign = RSA.verify(preSignStr, sign, AlipayConfig.ali_public_key, AlipayConfig.input_charset);
}
return isSign;
}
/**
* 获取远程服务器ATN结果,验证返回URL
* @param notify_id 通知校验ID
* @return 服务器ATN结果
* 验证结果集:
* invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
* true 返回正确信息
* false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String verifyResponse(String notify_id) {
//获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求
String partner = AlipayConfig.partner;
String veryfy_url = HTTPS_VERIFY_URL + "partner=" + partner + "¬ify_id=" + notify_id;
return checkUrl(veryfy_url);
}
/**
* 获取远程服务器ATN结果
* @param urlvalue 指定URL路径地址
* @return 服务器ATN结果
* 验证结果集:
* invalid命令参数不对 出现这个错误,请检测返回处理中partner和key是否为空
* true 返回正确信息
* false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
*/
private static String checkUrl(String urlvalue) {
String inputLine = "";
try {
URL url = new URL(urlvalue);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection
.getInputStream()));
inputLine = in.readLine().toString();
} catch (Exception e) {
e.printStackTrace();
inputLine = "";
}
return inputLine;
}
}
//生成订单号的一个方法
/**
* 返回系统当前时间(精确到毫秒),作为一个唯一的订单编号
* @return
* 以yyyyMMddHHmmss为格式的当前系统时间
*/
public static String getOrderNum(){
Date date=new Date();
DateFormat df=new SimpleDateFormat(dtLong);
return df.format(date);
}
下面是用到的几个工具类:
/*
* Copyright (C) 2010 The MobileSecurePay Project
* All right reserved.
* author: shiqun.shi@alipay.com
*/
public final class Base64 {
static private final int BASELENGTH = 128;
static private final int LOOKUPLENGTH = 64;
static private final int TWENTYFOURBITGROUP = 24;
static private final int EIGHTBIT = 8;
static private final int SIXTEENBIT = 16;
static private final int FOURBYTE = 4;
static private final int SIGN = -128;
static private final char PAD = '=';
static private final boolean fDebug = false;
static final private byte[] base64Alphabet = new byte[BASELENGTH];
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
static {
for (int i = 0; i < BASELENGTH; ++i) {
base64Alphabet[i] = -1;
}
for (int i = 'Z'; i >= 'A'; i--) {
base64Alphabet[i] = (byte) (i - 'A');
}
for (int i = 'z'; i >= 'a'; i--) {
base64Alphabet[i] = (byte) (i - 'a' + 26);
}
for (int i = '9'; i >= '0'; i--) {
base64Alphabet[i] = (byte) (i - '0' + 52);
}
base64Alphabet['+'] = 62;
base64Alphabet['/'] = 63;
for (int i = 0; i <= 25; i++) {
lookUpBase64Alphabet[i] = (char) ('A' + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('a' + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('0' + j);
}
lookUpBase64Alphabet[62] = (char) '+';
lookUpBase64Alphabet[63] = (char) '/';
}
private static boolean isWhiteSpace(char octect) {
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
}
private static boolean isPad(char octect) {
return (octect == PAD);
}
private static boolean isData(char octect) {
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
}
/**
* Encodes hex octects into Base64
*
* @param binaryData Array containing binaryData
* @return Encoded Base64 array
*/
public static String encode(byte[] binaryData) {
if (binaryData == null) {
return null;
}
int lengthDataBits = binaryData.length * EIGHTBIT;
if (lengthDataBits == 0) {
return "";
}
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
char encodedData[] = null;
encodedData = new char[numberQuartet * 4];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
if (fDebug) {
System.out.println("number of triplets = " + numberTriplets);
}
for (int i = 0; i < numberTriplets; i++) {
b1 = binaryData[dataIndex++];
b2 = binaryData[dataIndex++];
b3 = binaryData[dataIndex++];
if (fDebug) {
System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3);
}
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
if (fDebug) {
System.out.println("val2 = " + val2);
System.out.println("k4 = " + (k << 4));
System.out.println("vak = " + (val2 | (k << 4)));
}
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
}
// form integral number of 6-bit groups
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
if (fDebug) {
System.out.println("b1=" + b1);
System.out.println("b1<<2 = " + (b1 >> 2));
}
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex++] = PAD;
encodedData[encodedIndex++] = PAD;
} else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex++] = PAD;
}
return new String(encodedData);
}
/**
* Decodes Base64 data into octects
*
* @param encoded string containing Base64 data
* @return Array containind decoded data.
*/
public static byte[] decode(String encoded) {
if (encoded == null) {
return null;
}
char[] base64Data = encoded.toCharArray();
// remove white spaces
int len = removeWhiteSpace(base64Data);
if (len % FOURBYTE != 0) {
return null;//should be divisible by four
}
int numberQuadruple = (len / FOURBYTE);
if (numberQuadruple == 0) {
return new byte[0];
}
byte decodedData[] = null;
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
int i = 0;
int encodedIndex = 0;
int dataIndex = 0;
decodedData = new byte[(numberQuadruple) * 3];
for (; i < numberQuadruple - 1; i++) {
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
|| !isData((d3 = base64Data[dataIndex++]))
|| !isData((d4 = base64Data[dataIndex++]))) {
return null;
}//if found "no data" just return null
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
return null;//if found "no data" just return null
}
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
d3 = base64Data[dataIndex++];
d4 = base64Data[dataIndex++];
if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
if (isPad(d3) && isPad(d4)) {
if ((b2 & 0xf) != 0)//last 4 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 1];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
return tmp;
} else if (!isPad(d3) && isPad(d4)) {
b3 = base64Alphabet[d3];
if ((b3 & 0x3) != 0)//last 2 bits should be zero
{
return null;
}
byte[] tmp = new byte[i * 3 + 2];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
return tmp;
} else {
return null;
}
} else { //No PAD e.g 3cQl
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
return decodedData;
}
/**
* remove WhiteSpace from MIME containing encoded Base64 data.
*
* @param data the byte array of base64 data (with WS)
* @return the new length
*/
private static int removeWhiteSpace(char[] data) {
if (data == null) {
return 0;
}
// count characters that's not whitespace
int newSize = 0;
int len = data.length;
for (int i = 0; i < len; i++) {
if (!isWhiteSpace(data[i])) {
data[newSize++] = data[i];
}
}
return newSize;
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
public class RSA{
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
/**
* RSA签名
* @param content 待签名数据
* @param privateKey 商户私钥
* @param input_charset 编码格式
* @return 签名值
*/
public static String sign(String content, String privateKey, String input_charset)
{
try
{
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( Base64.decode(privateKey) );
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initSign(priKey);
signature.update( content.getBytes(input_charset) );
byte[] signed = signature.sign();
return Base64.encode(signed);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
/**
* RSA验签名检查
* @param content 待签名数据
* @param sign 签名值
* @param ali_public_key 支付宝公钥
* @param input_charset 编码格式
* @return 布尔值
*/
public static boolean verify(String content, String sign, String ali_public_key, String input_charset)
{
try
{
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = Base64.decode(ali_public_key);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature
.getInstance(SIGN_ALGORITHMS);
signature.initVerify(pubKey);
signature.update( content.getBytes(input_charset) );
boolean bverify = signature.verify( Base64.decode(sign) );
return bverify;
}
catch (Exception e)
{
e.printStackTrace();
}
return false;
}
/**
* 解密
* @param content 密文
* @param private_key 商户私钥
* @param input_charset 编码格式
* @return 解密后的字符串
*/
public static String decrypt(String content, String private_key, String input_charset) throws Exception {
PrivateKey prikey = getPrivateKey(private_key);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, prikey);
InputStream ins = new ByteArrayInputStream(Base64.decode(content));
ByteArrayOutputStream writer = new ByteArrayOutputStream();
//rsa解密的字节大小最多是128,将需要解密的内容,按128位拆开解密
byte[] buf = new byte[128];
int bufl;
while ((bufl = ins.read(buf)) != -1) {
byte[] block = null;
if (buf.length == bufl) {
block = buf;
} else {
block = new byte[bufl];
for (int i = 0; i < bufl; i++) {
block[i] = buf[i];
}
}
writer.write(cipher.doFinal(block));
}
return new String(writer.toByteArray(), input_charset);
}
/**
* 得到私钥
* @param key 密钥字符串(经过base64编码)
* @throws Exception
*/
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes;
keyBytes = Base64.decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
}
import java.util.Date;
import java.util.Random;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
/* *
*类名:UtilDate
*功能:自定义订单类
*详细:工具类,可以用作获取系统日期、订单编号等
*版本:3.3
*日期:2012-08-17
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/
public class UtilDate {
/** 年月日时分秒(无下划线) yyyyMMddHHmmss */
public static final String dtLong = "yyyyMMddHHmmss";
/** 完整时间 yyyy-MM-dd HH:mm:ss */
public static final String simple = "yyyy-MM-dd HH:mm:ss";
/** 年月日(无下划线) yyyyMMdd */
public static final String dtShort = "yyyyMMdd";
/**
* 返回系统当前时间(精确到毫秒),作为一个唯一的订单编号
* @return
* 以yyyyMMddHHmmss为格式的当前系统时间
*/
public static String getOrderNum(){
Date date=new Date();
DateFormat df=new SimpleDateFormat(dtLong);
return df.format(date);
}
/**
* 获取系统当前日期(精确到毫秒),格式:yyyy-MM-dd HH:mm:ss
* @return
*/
public static String getDateFormatter(){
Date date=new Date();
DateFormat df=new SimpleDateFormat(simple);
return df.format(date);
}
/**
* 获取系统当期年月日(精确到天),格式:yyyyMMdd
* @return
*/
public static String getDate(){
Date date=new Date();
DateFormat df=new SimpleDateFormat(dtShort);
return df.format(date);
}
/**
* 产生随机的三位数
* @return
*/
public static String getThree(){
Random rad=new Random();
return rad.nextInt(1000)+"";
}
}
下面才是主要的前段请求的servlet
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONException;
import org.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.ruchuapp.service.WxAndAliPayService;
import com.ruchuapp.tools.DateUtil;
/**
* 支付宝客户端请求参数链接
*/
@WebServlet("/alipayServlet")
public class alipayServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public alipayServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String cardid = request.getParameter("cardid");
String userid = request.getParameter("userid");
String mess = getMess(cardid,userid);
response.getWriter().print(mess);
}
private static String getMess(String cardid,String userid) {
String uid = WxAndAliPayService.getuid(userid);
JSONObject orderInfo = WxAndAliPayService.getOrderInfo(cardid);
JSONObject jo=new JSONObject();
try {
String cardname=orderInfo.getString("cardname");
String cardprice=orderInfo.getString("cardprice");
String cardtime=orderInfo.getString("cardtime");
String orderNum = UtilDate.getOrderNum();
//插入一条订单记录
WxAndAliPayService.insertOrderInfo(cardname,uid,orderNum,cardprice,"支付宝支付");
//公共参数
Map<String, String> map = new HashMap<String, String>();
map.put("app_id", AlipayConfig.app_id);
map.put("method", "alipay.trade.app.pay");
map.put("format", "json");
map.put("charset", "utf-8");
map.put("sign_type", "RSA");
map.put("timestamp", DateUtil.getTimestamp());
map.put("version", "1.0");
map.put("notify_url", AlipayConfig.service);
Map<String, String> m = new HashMap<String, String>();
m.put("body", "购买会员过程");//对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。
m.put("subject", "购买会员");//商品的标题/交易标题/订单标题/订单关键字等。
m.put("out_trade_no",orderNum);//商户网站唯一订单号
m.put("timeout_express", "30m");//设置超时时间
m.put("total_amount", cardprice);//价格
m.put("seller_id", AlipayConfig.partner);//收款商户支付宝id
m.put("product_code", "QUICK_MSECURITY_PAY");
JSONObject bizcontentJson=new JSONObject(m);
map.put("biz_content", bizcontentJson.toString());
//对未签名原始字符串进行签名
String rsaSign=null;
String json4=null;
rsaSign = AlipaySignature.rsaSign(map, AlipayConfig.private_key, "utf-8");
Map<String, String> map4 = new HashMap<String, String>();
map4.put("app_id", AlipayConfig.app_id);
map4.put("method", "alipay.trade.app.pay");
map4.put("format", "json");
map4.put("charset", "utf-8");
//map4.put("sign_type",AlipayConfig.sign_type );
map4.put("timestamp", URLEncoder.encode(DateUtil.getTimestamp(),"UTF-8"));
map4.put("version", "1.0");
map4.put("notify_url", URLEncoder.encode(AlipayConfig.service,"UTF-8"));
//最后对请求字符串的所有一级value(biz_content作为一个value)进行encode,编码格式按请求串中的charset为准,没传charset按UTF-8处理
map4.put("biz_content", URLEncoder.encode(bizcontentJson.toString(), "UTF-8"));
Map par = AlipayCore.paraFilter(map4); //除去数组中的空值和签名参数
json4= AlipayCore.createLinkString(par); //拼接后的字符串
json4=json4 +"&sign_type=RSA&sign=" + URLEncoder.encode(rsaSign, "UTF-8");
jo.put("info", json4);
} catch (Exception e) {
e.printStackTrace();
}
return jo.toString();
}
}
异步回掉地址处理订单完成的业务
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONException;
import org.json.JSONObject;
import com.ruchuapp.service.WxAndAliPayService;
import com.ruchuapp.servlet.my.BuyVIPServlet;
/**
* 支付宝支付后通知回调请求
*/
@WebServlet("/CallbackServlet")
public class CallbackServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public CallbackServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map requestParams = request.getParameterMap();
PrintWriter out = response.getWriter();
String mess = getMess(requestParams);
out.print(mess);
}
private String getMess(Map requestParams) {
String flag="";
Map<String,String> map=new HashMap<String, String>();
for(Iterator iter=requestParams.keySet().iterator();iter.hasNext();){
String name=(String)iter.next();
String[] values=(String[]) requestParams.get(name);
String valueStr="";
for(int i=0;i<values.length;i++){
valueStr=(i==values.length-1)?valueStr+values[i]:valueStr+values[i]+",";
}
map.put(name,valueStr);//将post过来的数据放到map中方便签名认证
}
String out_trade_no=map.get("out_trade_no");
String trade_status=map.get("trade_status");
//商户订单号
if(AlipayNotify.verify(map)){//验证签名
if(trade_status.equals("TRADE_SUCCESS")) {//交易成功
//更改订单状态
boolean updateOrderInfo = WxAndAliPayService.updateOrderInfo(out_trade_no);
if(updateOrderInfo){
//更新成功,应该将这个订单放到usercost
Map<String, String> orderinfo = WxAndAliPayService.getorderinfo(out_trade_no);
//插入到usercost表中
boolean insertOrderInfo = WxAndAliPayService.insertOrderInfo(orderinfo);
if(insertOrderInfo){//订单完成
//查找到user表中的user_id和会员天数
Map<String, String> useridAndDays = WxAndAliPayService.getUseridAndDays(orderinfo.get("userid").toString(),orderinfo.get("costname").toString());
//将user表中的vip字段更新
BuyVIPServlet.execute(useridAndDays.get("userid").toString(),Integer.parseInt(useridAndDays.get("cardtime")));
}
}
flag="success";
}else {
//支付失败
flag="fail";
}
}
return flag;
}
}
这些暂时还没有放到公网服务器测试,只是在本地测试可以调用支付宝并支付,数据库订单的一些回调业务还没有测试