IOS IAP APP内支付 Java服务端代码

 

场景:作为后台需要为app提供服务,在ios中,app内进行支付购买时需要进行二次验证。

基础:可以参考上一篇转载的博文In-App Purchase(iap)快速指南了解原理。

直接先上服务端测试通过的代码:

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import com.alibaba.fastjson.JSONObject; @Controller
@RequestMapping("iap")
public class IapController { //购买凭证验证地址
private static final String certificateUrl = "https://buy.itunes.apple.com/verifyReceipt"; //测试的购买凭证验证地址
private static final String certificateUrlTest = "https://sandbox.itunes.apple.com/verifyReceipt"; /**
* 重写X509TrustManager
*/
private static TrustManager myX509TrustManager = new X509TrustManager() { @Override
public X509Certificate[] getAcceptedIssuers() {
return null;
} @Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
}; /**
* 接收iOS端发过来的购买凭证
* @param userId
* @param receipt
* @param chooseEnv
*/
@RequestMapping("/setIapCertificate")
public String setIapCertificate(String userId, String receipt, boolean chooseEnv){
if(StringUtils.isEmpty(userId) || StringUtils.isEmpty(receipt)){
return null;
}
String url = null;
url = chooseEnv == true? certificateUrl:certificateUrlTest;
final String certificateCode = receipt;
if(StringUtils.isNotEmpty(certificateCode)){
return sendHttpsCoon(url, certificateCode);
}else{
return null;
}
} /**
* 发送请求
* @param url
* @param strings
* @return
*/
private String sendHttpsCoon(String url, String code){
if(url.isEmpty()){
return null;
}
try {
//设置SSLContext
SSLContext ssl = SSLContext.getInstance("SSL");
ssl.init(null, new TrustManager[]{myX509TrustManager}, null); //打开连接
HttpsURLConnection conn = (HttpsURLConnection) new URL(url).openConnection();
//设置套接工厂
conn.setSSLSocketFactory(ssl.getSocketFactory());
//加入数据
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-type","application/json"); JSONObject obj = new JSONObject();
obj.put("receipt-data", code); BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
buffOutStr.write(obj.toString().getBytes());
buffOutStr.flush();
buffOutStr.close(); //获取输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line = null;
StringBuffer sb = new StringBuffer();
while((line = reader.readLine())!= null){
sb.append(line);
}
return sb.toString(); } catch (Exception e) {
return null;
}
}
}

注意:
1. setIapCertificate()方法的输入输出参数自行与ios商定,这里是直接将apple返回的数据返给了app(个人推荐返回boolean,前台只要知道验证是否通过即可)。
2. 参数receipt:BASE64编码过的话
ewoJInNpZ25hdHVyZSIgPSAiQXAzbjVUZmQrVXIvQmFrTEU3VG9Dc3NOSGdxbmI2dnlrM0RLZlFxcnBxQStLV1AzVHVkZDN2N2w0OUEyc2NVY1g2cWNDeEx3bHAzR2RKMG9Ma3IzRzdTaEZFYU5UeTBrL25hRTNoWUIxY0kxajFGM1ppSmxTVC9kL1VDN0ZFY1BUMjR2SUFFZDE2WXFOdHNqWUpNYmJQVGRRR3g4NitFZWlDR21mVDJKS0VrK0FBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NCdXA0K1BBaG0vTE1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEUwTURZd056QXdNREl5TVZvWERURTJNRFV4T0RFNE16RXpNRm93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNbVRFdUxnamltTHdSSnh5MW9FZjBlc1VORFZFSWU2d0Rzbm5hbDE0aE5CdDF2MTk1WDZuOTNZTzdnaTNvclBTdXg5RDU1NFNrTXArU2F5Zzg0bFRjMzYyVXRtWUxwV25iMzRucXlHeDlLQlZUeTVPR1Y0bGpFMU93QytvVG5STStRTFJDbWVOeE1iUFpoUzQ3VCtlWnRERWhWQjl1c2szK0pNMkNvZ2Z3bzdBZ01CQUFHamNqQndNQjBHQTFVZERnUVdCQlNKYUVlTnVxOURmNlpmTjY4RmUrSTJ1MjJzc0RBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkRZZDZPS2RndElCR0xVeWF3N1hRd3VSV0VNNk1BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQWVhSlYyVTUxcnhmY3FBQWU1QzIvZkVXOEtVbDRpTzRsTXV0YTdONlh6UDFwWkl6MU5ra0N0SUl3ZXlOajVVUllISytIalJLU1U5UkxndU5sMG5rZnhxT2JpTWNrd1J1ZEtTcTY5Tkluclp5Q0Q2NlI0Szc3bmI5bE1UQUJTU1lsc0t0OG9OdGxoZ1IvMWtqU1NSUWNIa3RzRGNTaVFHS01ka1NscDRBeVhmN3ZuSFBCZTR5Q3dZVjJQcFNOMDRrYm9pSjNwQmx4c0d3Vi9abEwyNk0ydWVZSEtZQ3VYaGRxRnd4VmdtNTJoM29lSk9PdC92WTRFY1FxN2VxSG02bTAzWjliN1BSellNMktHWEhEbU9Nazd2RHBlTVZsTERQU0dZejErVTNzRHhKemViU3BiYUptVDdpbXpVS2ZnZ0VZN3h4ZjRjemZIMHlqNXdOelNHVE92UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREUyTFRBMExUSTRJREF6T2pFNE9qUTVJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluVnVhWEYxWlMxcFpHVnVkR2xtYVdWeUlpQTlJQ0prTkdVM01qRmxZelkzWldZeVptVmpZVGRtWW1SaVpESTFZVFExWTJaaU16ZGxNVEJsWVRkaUlqc0tDU0p2Y21sbmFXNWhiQzEwY21GdWMyRmpkR2x2YmkxcFpDSWdQU0FpTVRBd01EQXdNREl3T0RZeU1EUTNNQ0k3Q2draVluWnljeUlnUFNBaU1TNHhJanNLQ1NKMGNtRnVjMkZqZEdsdmJpMXBaQ0lnUFNBaU1UQXdNREF3TURJd09EWXlNRFEzTUNJN0Nna2ljWFZoYm5ScGRIa2lJRDBnSWpFaU93b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVdGJYTWlJRDBnSWpFME5qRTRNemczTWpreU9EVWlPd29KSW5WdWFYRjFaUzEyWlc1a2IzSXRhV1JsYm5ScFptbGxjaUlnUFNBaU9FVXhPVVZGUXpRdE16TkVOeTAwTlRNMkxVSTJNa1V0TVRFeVFrRkROamhGUlVORUlqc0tDU0p3Y205a2RXTjBMV2xrSWlBOUlDSXhNalEwSWpzS0NTSnBkR1Z0TFdsa0lpQTlJQ0l4TVRBNE56azRNVFV4SWpzS0NTSmlhV1FpSUQwZ0ltTnZiUzVrYjJOMGIzSkllWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakUwTmpFNE16ZzNNamt5T0RVaU93b0pJbkIxY21Ob1lYTmxMV1JoZEdVaUlEMGdJakl3TVRZdE1EUXRNamdnTVRBNk1UZzZORGtnUlhSakwwZE5WQ0k3Q2draWNIVnlZMmhoYzJVdFpHRjBaUzF3YzNRaUlEMGdJakl3TVRZdE1EUXRNamdnTURNNk1UZzZORGtnUVcxbGNtbGpZUzlNYjNOZlFXNW5aV3hsY3lJN0Nna2liM0pwWjJsdVlXd3RjSFZ5WTJoaGMyVXRaR0YwWlNJZ1BTQWlNakF4Tmkwd05DMHlPQ0F4TURveE9EbzBPU0JGZEdNdlIwMVVJanNLZlE9PSI7CgkiZW52aXJvbm1lbnQiID0gIlNhbmRib3giOwoJInBvZCIgPSAiMTAwIjsKCSJzaWduaW5nLXN0YXR1cyIgPSAiMCI7Cn0=
未进行编码的话:
{
"signature" = "Ap3n5Tfd+Ur/BakLE7ToCssNHgqnb6vyk3DKfQqrpqA+KWP3Tudd3v7l49A2scUcX6qcCxLwlp3GdJ0oLkr3G7ShFEaNTy0k/naE3hYB1cI1j1F3ZiJlST/d/UC7FEcPT24vIAEd16YqNtsjYJMbbPTdQGx86+EeiCGmfT2JKEk+AAADVzCCA1MwggI7oAMCAQICCBup4+PAhm/LMA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEzMDEGA1UEAwwqQXBwbGUgaVR1bmVzIFN0b3JlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE0MDYwNzAwMDIyMVoXDTE2MDUxODE4MzEzMFowZDEjMCEGA1UEAwwaUHVyY2hhc2VSZWNlaXB0Q2VydGlmaWNhdGUxGzAZBgNVBAsMEkFwcGxlIGlUdW5lcyBTdG9yZTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMmTEuLgjimLwRJxy1oEf0esUNDVEIe6wDsnnal14hNBt1v195X6n93YO7gi3orPSux9D554SkMp+Sayg84lTc362UtmYLpWnb34nqyGx9KBVTy5OGV4ljE1OwC+oTnRM+QLRCmeNxMbPZhS47T+eZtDEhVB9usk3+JM2Cogfwo7AgMBAAGjcjBwMB0GA1UdDgQWBBSJaEeNuq9Df6ZfN68Fe+I2u22ssDAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFDYd6OKdgtIBGLUyaw7XQwuRWEM6MA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgUBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEAeaJV2U51rxfcqAAe5C2/fEW8KUl4iO4lMuta7N6XzP1pZIz1NkkCtIIweyNj5URYHK+HjRKSU9RLguNl0nkfxqObiMckwRudKSq69NInrZyCD66R4K77nb9lMTABSSYlsKt8oNtlhgR/1kjSSRQcHktsDcSiQGKMdkSlp4AyXf7vnHPBe4yCwYV2PpSN04kboiJ3pBlxsGwV/ZlL26M2ueYHKYCuXhdqFwxVgm52h3oeJOOt/vY4EcQq7eqHm6m03Z9b7PRzYM2KGXHDmOMk7vDpeMVlLDPSGYz1+U3sDxJzebSpbaJmT7imzUKfggEY7xxf4czfH0yj5wNzSGTOvQ==";
"purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDE2LTA0LTI4IDAzOjE4OjQ5IEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICJkNGU3MjFlYzY3ZWYyZmVjYTdmYmRiZDI1YTQ1Y2ZiMzdlMTBlYTdiIjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDIwODYyMDQ3MCI7CgkiYnZycyIgPSAiMS4xIjsKCSJ0cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDIwODYyMDQ3MCI7CgkicXVhbnRpdHkiID0gIjEiOwoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtbXMiID0gIjE0NjE4Mzg3MjkyODUiOwoJInVuaXF1ZS12ZW5kb3ItaWRlbnRpZmllciIgPSAiOEUxOUVFQzQtMzNENy00NTM2LUI2MkUtMTEyQkFDNjhFRUNEIjsKCSJwcm9kdWN0LWlkIiA9ICIxMjQ0IjsKCSJpdGVtLWlkIiA9ICIxMTA4Nzk4MTUxIjsKCSJiaWQiID0gImNvbS5kb2N0b3JIeXMiOwoJInB1cmNoYXNlLWRhdGUtbXMiID0gIjE0NjE4Mzg3MjkyODUiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMTYtMDQtMjggMTA6MTg6NDkgRXRjL0dNVCI7CgkicHVyY2hhc2UtZGF0ZS1wc3QiID0gIjIwMTYtMDQtMjggMDM6MTg6NDkgQW1lcmljYS9Mb3NfQW5nZWxlcyI7Cgkib3JpZ2luYWwtcHVyY2hhc2UtZGF0ZSIgPSAiMjAxNi0wNC0yOCAxMDoxODo0OSBFdGMvR01UIjsKfQ==";
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}
3.  向苹果发起验证请求的参数一定要通过json格式发送:
conn.setRequestProperty("Content-type","application/json");
JSONObject obj = new JSONObject();
obj.put("receipt-data", code);
4.  App Store返回值也是一个JSON对象:
{
“status” : 0,
“receipt” : { … }
}
具体我这边是这样的:
<pre name="code" class="javascript">{
"receipt": {
"original_purchase_date_pst": "2016-04-28 03:18:49 America/Los_Angeles",
"purchase_date_ms": "1461838729285",
"unique_identifier": "d4e721ec67ef2feca7fbdbd25a45cfb37e10ea7b",
"original_transaction_id": "1000000208620470",
"bvrs": "1.1",
"transaction_id": "1000000208620470",
"quantity": "1",
"unique_vendor_identifier": "8E19EEC4-33D7-4536-B62E-112BAC68EECD",
"item_id": "1108798151",
"product_id": "1244",
"purchase_date": "2016-04-28 10:18:49 Etc/GMT",
"original_purchase_date": "2016-04-28 10:18:49 Etc/GMT",
"purchase_date_pst": "2016-04-28 03:18:49 America/Los_Angeles",
"bid": "com.doctorHys",
"original_purchase_date_ms": "1461838729285"
},
"status": 0
}

IOS IAP APP内支付 Java服务端代码的更多相关文章

  1. app支付宝支付java服务端代码

    app支付java服务端生成支付单文档 public String aliPay(String amount,Map<String,Object> body){ //实例化客户端 Alip ...

  2. app微信支付-java服务端接口 支付-查询-退款

    个人不怎么看得懂微信的文档,看了很多前辈的写法,终于调通了,在这里做一下记录. 首先来定义各种处理类(微信支付不需要特殊jar包,很多处理需要自己封装,当然也可以自己写完打个jar包) 参数要用jdo ...

  3. 基于APNs最新HTTP/2接口实现iOS的高性能消息推送(服务端篇)

    1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ...

  4. 支付宝ios支付请求Java服务端签名报的一个错(ALI40247) 原创

    今天做app的支付宝支付,遇到些问题,以前做支付宝支付签名都是直接在客户端App进行,今天下了最新版本ios的支付宝支付demo,运行demo时底部有红色的显眼字体,告知用户签名必须在服务端进行... ...

  5. “快的打车”创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - V2EX

    "快的打车"创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - ...

  6. java服务端微信小程序支付

    发布时间:2018-10-05   技术:springboot+maven   概述 java微信小程序demo支付只需配置支付一下参数即可运行 详细 代码下载:http://www.demodash ...

  7. Java 服务端监控方案(四. Java 篇)

    http://jerrypeng.me/2014/08/08/server-side-java-monitoring-java/ 这个漫长的系列文章今天要迎来最后一篇了,也是真正与 Java 有关的部 ...

  8. 那些年,我们见过的 Java 服务端“问题”

    导读 明代著名的心学集大成者王阳明先生在<传习录>中有云: 道无精粗,人之所见有精粗.如这一间房,人初进来,只见一个大规模如此.处久,便柱壁之类,一一看得明白.再久,如柱上有些文藻,细细都 ...

  9. java服务端集成极光消息推送--详细开发步骤

    1.极光推送账号准备 要使用极光消息推送必须先在官方网站上注册账号,并添加应用. 产品介绍:https://docs.jiguang.cn/jpush/guideline/intro/ 注册开发者账号 ...

随机推荐

  1. A^B Mod C (快速幂)

    题目描述: 给出3个正整数A B C,求A^B Mod C. 例如,3 5 8,3^5 Mod 8 = 3. Input3个正整数A B C,中间用空格分隔.(1 <= A,B,C <= ...

  2. centos7的redis加哨兵系统

    三台服务器 1.下载 wget http://download.redis.io/releases/redis-5.0.3.tar.gztar -zxvf redis-5.0.3.tar.gzcd r ...

  3. chrome 截取整个网页

  4. 【00NOIP提高组】单词接龙

    #include<bits/stdc++.h> using namespace std; ; int n,length; int vis[N]; string str[N]; inline ...

  5. Python geometry_msgs.msg.PoseStamped() Examples

    https://www.programcreek.com/python/example/70252/geometry_msgs.msg.PoseStampedhttps://programtalk.c ...

  6. java生成临时文件夹和删除临时文件夹

    为了不生成同名的文件夹 String s = UUID.randomUUID().toString(); String filepath = ServletActionContext.getServl ...

  7. Linux 如何通过某一台服务器调用执行多台远程服务器上的脚本,结果显示在本地?

    现在都流行自动化运维了,可能目前技术不够,很多自动化工具还不怎么会用,所以本次只是通过ssh来实现功能. 说明:自己写的一个简单脚本,只是实现了基础功能,还有待优化. 一共三台机器: master:1 ...

  8. MongoDB下载不了的问题

    官网上,mongoDB数据库下载不了,搜了半天,在知乎上找到答案:在MongoDB download center中选择community-server,拖到底部有一个all versions bin ...

  9. Redis客户端多线程跟多个连接不是一回事

    先抱怨一波,大国庆节的放假前一天的下班前15分钟,通知让我加班,因为一个Redis的bug,而这个bug我在1个半小时之前刚听说了个大概,心里很不情愿: 好了,说正事: 问题现象: bug是这样的,两 ...

  10. Get Argument Values From Linq Expression

    Get Argument Values From Linq Expression If you even find yourself unpacking an expression in C#, yo ...