初始化项目

This commit is contained in:
BBIT-Kai
2026-04-30 10:47:26 +08:00
commit c932419c73
147 changed files with 45298 additions and 0 deletions
@@ -0,0 +1,66 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleDemo
{
public class EncryptDes
{
/**
* aStrString 加密内容
* aStrKey 加密秘钥
*/
public static String Encrypt3Des(String aStrString, String aStrKey, CipherMode mode = CipherMode.ECB, String iv = "12345678")
{
try
{
var des = new TripleDESCryptoServiceProvider
{
Key = Encoding.UTF8.GetBytes(aStrKey),
Mode = mode
};
if (mode == CipherMode.CBC)
{
des.IV = Encoding.UTF8.GetBytes(iv);
}
var desEncrypt = des.CreateEncryptor();
byte[] buffer = Encoding.UTF8.GetBytes(aStrString);
return Convert.ToBase64String(desEncrypt.TransformFinalBlock(buffer, 0, buffer.Length));
}
catch (Exception e)
{
return string.Empty;
}
}
public static string Decrypt3Des(string aStrString, string aStrKey, CipherMode mode = CipherMode.ECB, string iv = "12345678")
{
try
{
var des = new TripleDESCryptoServiceProvider
{
Key = Encoding.UTF8.GetBytes(aStrKey),
Mode = mode,
Padding = PaddingMode.PKCS7
};
if (mode == CipherMode.CBC)
{
des.IV = Encoding.UTF8.GetBytes(iv);
}
var desDecrypt = des.CreateDecryptor();
var result = "";
byte[] buffer = Convert.FromBase64String(aStrString);
result = Encoding.UTF8.GetString(desDecrypt.TransformFinalBlock(buffer, 0, buffer.Length));
return result;
}
catch (Exception e)
{
return string.Empty;
}
}
}
}
@@ -0,0 +1,37 @@
using System.IO;
using System.Net;
using System.Text;
namespace ConsoleDemo
{
public class PostJson
{
/**
* Json的请求头 post请求地址
*/
public static string Post4Json(string url,string buildRequest)
{
string result = "";
HttpWebRequest request =(HttpWebRequest) WebRequest.Create(url);
request.Method = "POST";
request.Timeout = 5000;
request.ContentType = "application/json";
byte[] byte4builde = Encoding.UTF8.GetBytes(buildRequest);
request.ContentLength = byte4builde.Length;
using (Stream reqStream=request.GetRequestStream())
{
reqStream.Write(byte4builde,0,byte4builde.Length);
reqStream.Close();
}
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
Stream stream = response.GetResponseStream();
//获得响应内容
using (StreamReader reader=new StreamReader(stream,Encoding.UTF8))
{
result = reader.ReadToEnd();
}
return result;
}
}
}
@@ -0,0 +1,4 @@
// See https://aka.ms/new-console-template for more information
using ConsoleDemo;
Console.WriteLine("Hello, World!");
@@ -0,0 +1,123 @@
using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Text;
namespace ConsoleDemo
{
public class PublicData
{
/**
* 公共报文组装
*/
public static string publicparam(string content,string platformCode ,
string platformAlias,string privateKey,string password)
{
StringBuilder sign =new StringBuilder();
string contentstr=EncryptDes.Encrypt3Des(content, password);
sign.Append("content="+contentstr+"&");
sign.Append("format=JSON&");
sign.Append("platformCode="+platformCode+"&");
string time = DateTime.Now.ToString("yyyyMMddHHmmss");
string serianlNo = platformAlias + time + GenerateCheckCode(8);
sign.Append("serialNo="+serianlNo+"&");
sign.Append("signType=RSA&");
string timestamp=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
sign.Append("timestamp="+timestamp+"&");
sign.Append("version=1.0");
string rsasign = RSA.sign(sign.ToString(), privateKey);
Hashtable publictable=new Hashtable();
publictable.Add("sign",rsasign);
publictable.Add("format","JSON");
publictable.Add("platformCode",platformCode);
publictable.Add("serialNo",serianlNo);
publictable.Add("signType","RSA");
publictable.Add("timestamp",timestamp);
publictable.Add("version","1.0");
publictable.Add("content",contentstr);
return ToJson.Table2Json(publictable);
}
public static string disposeResponse(string str,string ptpublickey, string deskey)
{
Hashtable strtable=new Hashtable();
StringBuilder content=new StringBuilder();
Hashtable dispose=new Hashtable();
string sign = "";
string serialNo = "";
str=str.Replace("{","").Replace("}","").Replace("\"","").Replace(",","&").Replace(":","&");
string[] arraystr = str.Split('&');
for (int i = 0; i < arraystr.Length-1; i+=2)
{
if (arraystr[i]=="sign")
{
sign = arraystr[i + 1];
}
if (arraystr[i]=="serialNo")
{
serialNo=arraystr[i + 1];
}
strtable.Add(arraystr[i],arraystr[i+1]);
}
strtable.Remove("serialNo");
strtable.Remove("sign");
ArrayList ke = new ArrayList(strtable.Keys);
ke.Sort();
foreach (string tableEntry in ke)
{
content.Append(string.Format("{0}={1}&", tableEntry, strtable[tableEntry]));
}
content.Append("serialNo=" + serialNo);
bool res=RSA.verify(content.ToString(), sign, ptpublickey, "UTF-8");
Console.WriteLine(res);
if (res)
{
for (int i = 0; i < arraystr.Length - 1; i += 2)
{
if (arraystr[i] == "content")
{
arraystr[i+1]=EncryptDes.Decrypt3Des(arraystr[i+1],deskey);
}
dispose.Add(arraystr[i],arraystr[i+1]);
}
return ToJson.Table2Json(dispose);
}
else
{
return "验签失败";
}
}
/**
* 随机数生成
*/
private static string GenerateCheckCode(int codeCount)
{
int rep = 0;
string str = string.Empty;
long num2 = DateTime.Now.Ticks + rep;
rep++;
Random random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> rep)));
for (int i = 0; i < codeCount; i++)
{
char ch;
int num = random.Next();
if ((num % 2) == 0)
{
ch = (char)(0x30 + ((ushort)(num % 10)));
}
else
{
ch = (char)(0x41 + ((ushort)(num % 0x1a)));
}
str = str + ch.ToString();
}
return str;
}
}
}
@@ -0,0 +1,502 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
namespace ConsoleDemo
{
internal class RSA
{
/**
* content 签名前的报文
* privateKey 私钥
* input_charset 编码格式 (以下默认UTF-8)
*/
public static string sign(string content, string privateKey)
{
byte[] Data = Encoding.GetEncoding("UTF-8").GetBytes(content);
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
SHA1 sh = new SHA1CryptoServiceProvider();
byte[] signData = rsa.SignData(Data, sh);
return Convert.ToBase64String(signData);
}
/// <summary>
/// 验签
/// </summary>
/// <param name="content">待验签字符串</param>
/// <param name="signedString">签名</param>
/// <param name="publicKey">公钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns>true(通过)false(不通过)</returns>
public static bool verify(string content, string signedString, string publicKey, string input_charset)
{
bool result ;
byte[] Data = Encoding.GetEncoding(input_charset).GetBytes(content);
byte[] data = Convert.FromBase64String(signedString);
RSAParameters paraPub = ConvertFromPublicKey(publicKey);
RSACryptoServiceProvider rsaPub = new RSACryptoServiceProvider();
rsaPub.ImportParameters(paraPub);
SHA1 sh = new SHA1CryptoServiceProvider();
result = rsaPub.VerifyData(Data, sh, data);
return result;
}
/// <summary>
/// 加密
/// </summary>
/// <param name="resData">需要加密的字符串</param>
/// <param name="publicKey">公钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns>明文</returns>
public static string encryptData(string resData, string publicKey, string input_charset)
{
byte[] DataToEncrypt = Encoding.ASCII.GetBytes(resData);
string result = encrypt(DataToEncrypt, publicKey, input_charset);
return result;
}
/// <summary>
/// 解密
/// </summary>
/// <param name="resData">加密字符串</param>
/// <param name="privateKey">私钥</param>
/// <param name="input_charset">编码格式</param>
/// <returns>明文</returns>
public static string decryptData(string resData, string privateKey, string input_charset)
{
byte[] DataToDecrypt = Convert.FromBase64String(resData);
string result = "";
for (int j = 0; j < DataToDecrypt.Length / 128; j++)
{
byte[] buf = new byte[128];
for (int i = 0; i < 128; i++)
{
buf[i] = DataToDecrypt[i + 128 * j];
}
result += decrypt(buf, privateKey, input_charset);
}
return result;
}
#region
private static string encrypt(byte[] data, string publicKey, string input_charset)
{
RSACryptoServiceProvider rsa = DecodePemPublicKey(publicKey);
SHA1 sh = new SHA1CryptoServiceProvider();
byte[] result = rsa.Encrypt(data, false);
return Convert.ToBase64String(result);
}
private static string decrypt(byte[] data, string privateKey, string input_charset)
{
string result = "";
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
SHA1 sh = new SHA1CryptoServiceProvider();
byte[] source = rsa.Decrypt(data, false);
char[] asciiChars = new char[Encoding.GetEncoding(input_charset).GetCharCount(source, 0, source.Length)];
Encoding.GetEncoding(input_charset).GetChars(source, 0, source.Length, asciiChars, 0);
result = new string(asciiChars);
//result = ASCIIEncoding.ASCII.GetString(source);
return result;
}
private static RSACryptoServiceProvider DecodePemPublicKey(String pemstr)
{
byte[] pkcs8publickkey;
pkcs8publickkey = Convert.FromBase64String(pemstr);
if (pkcs8publickkey != null)
{
RSACryptoServiceProvider rsa = DecodeRSAPublicKey(pkcs8publickkey);
return rsa;
}
else
return null;
}
private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr)
{
byte[] pkcs8privatekey;
pkcs8privatekey = Convert.FromBase64String(pemstr);
if (pkcs8privatekey != null)
{
RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);
return rsa;
}
else
return null;
}
private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)
{
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
MemoryStream mem = new MemoryStream(pkcs8);
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if (bt != 0x02)
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0001)
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
bt = binr.ReadByte();
if (bt != 0x04) //expect an Octet string
return null;
bt = binr.ReadByte(); //read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count
if (bt == 0x81)
binr.ReadByte();
else
if (bt == 0x82)
binr.ReadUInt16();
//------ at this stage, the remaining sequence should be the RSA private key
byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
return rsacsp;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static bool CompareBytearrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
int i = 0;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
}
private static RSACryptoServiceProvider DecodeRSAPublicKey(byte[] publickey)
{
// encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[15];
// --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
MemoryStream mem = new MemoryStream(publickey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
seq = binr.ReadBytes(15); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8203)
binr.ReadInt16(); //advance 2 bytes
else
return null;
bt = binr.ReadByte();
if (bt != 0x00) //expect null byte next
return null;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
byte lowbyte = 0x00;
byte highbyte = 0x00;
if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
else if (twobytes == 0x8202)
{
highbyte = binr.ReadByte(); //advance 2 bytes
lowbyte = binr.ReadByte();
}
else
return null;
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
int modsize = BitConverter.ToInt32(modint, 0);
byte firstbyte = binr.ReadByte();
binr.BaseStream.Seek(-1, SeekOrigin.Current);
if (firstbyte == 0x00)
{ //if first byte (highest order) of modulus is zero, don't include it
binr.ReadByte(); //skip this null byte
modsize -= 1; //reduce modulus buffer size by 1
}
byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
return null;
int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
byte[] exponent = binr.ReadBytes(expbytes);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
// --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = 0;
ushort twobytes = 0;
int elems = 0;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null;
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null;
//------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems);
elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems);
// ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
}
private static int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt; // we already have the data size
}
while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
return count;
}
#endregion
#region .net Pem
private static RSAParameters ConvertFromPublicKey(string pemFileConent)
{
byte[] keyData = Convert.FromBase64String(pemFileConent);
if (keyData.Length < 162)
{
throw new ArgumentException("pem file content is incorrect.");
}
byte[] pemModulus = new byte[128];
byte[] pemPublicExponent = new byte[3];
Array.Copy(keyData, 29, pemModulus, 0, 128);
Array.Copy(keyData, 159, pemPublicExponent, 0, 3);
RSAParameters para = new RSAParameters();
para.Modulus = pemModulus;
para.Exponent = pemPublicExponent;
return para;
}
private static RSAParameters ConvertFromPrivateKey(string pemFileConent)
{
byte[] keyData = Convert.FromBase64String(pemFileConent);
if (keyData.Length < 609)
{
throw new ArgumentException("pem file content is incorrect.");
}
int index = 11;
byte[] pemModulus = new byte[128];
Array.Copy(keyData, index, pemModulus, 0, 128);
index += 128;
index += 2;//141
byte[] pemPublicExponent = new byte[3];
Array.Copy(keyData, index, pemPublicExponent, 0, 3);
index += 3;
index += 4;//148
byte[] pemPrivateExponent = new byte[128];
Array.Copy(keyData, index, pemPrivateExponent, 0, 128);
index += 128;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//279
byte[] pemPrime1 = new byte[64];
Array.Copy(keyData, index, pemPrime1, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//346
byte[] pemPrime2 = new byte[64];
Array.Copy(keyData, index, pemPrime2, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//412/413
byte[] pemExponent1 = new byte[64];
Array.Copy(keyData, index, pemExponent1, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//479/480
byte[] pemExponent2 = new byte[64];
Array.Copy(keyData, index, pemExponent2, 0, 64);
index += 64;
index += ((int)keyData[index + 1] == 64 ? 2 : 3);//545/546
byte[] pemCoefficient = new byte[64];
Array.Copy(keyData, index, pemCoefficient, 0, 64);
RSAParameters para = new RSAParameters();
para.Modulus = pemModulus;
para.Exponent = pemPublicExponent;
para.D = pemPrivateExponent;
para.P = pemPrime1;
para.Q = pemPrime2;
para.DP = pemExponent1;
para.DQ = pemExponent2;
para.InverseQ = pemCoefficient;
return para;
}
#endregion
}
}
@@ -0,0 +1,277 @@
using System;
using System.Collections;
using System.Text;
namespace ConsoleDemo
{
public class StarDemo
{
//私钥(与发给票通的公钥为一对)
private static String privateKey =
"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIVLAoolDaE7m5oMB1ZrILHkMXMF6qmC8I/FCejz4hwBcj59H3rbtcycBEmExOJTGwexFkNgRakhqM+3uP3VybWu1GBYNmqVzggWKKzThul9VPE3+OTMlxeG4H63RsCO1//J0MoUavXMMkL3txkZBO5EtTqek182eePOV8fC3ZxpAgMBAAECgYBp4Gg3BTGrZaa2mWFmspd41lK1E/kPBrRA7vltMfPj3P47RrYvp7/js/Xv0+d0AyFQXcjaYelTbCokPMJT1nJumb2A/Cqy3yGKX3Z6QibvByBlCKK29lZkw8WVRGFIzCIXhGKdqukXf8RyqfhInqHpZ9AoY2W60bbSP6EXj/rhNQJBAL76SmpQOrnCI8Xu75di0eXBN/bE9tKsf7AgMkpFRhaU8VLbvd27U9vRWqtu67RY3sOeRMh38JZBwAIS8tp5hgcCQQCyrOS6vfXIUxKoWyvGyMyhqoLsiAdnxBKHh8tMINo0ioCbU+jc2dgPDipL0ym5nhvg5fCXZC2rvkKUltLEqq4PAkAqBf9b932EpKCkjFgyUq9nRCYhaeP6JbUPN3Z5e1bZ3zpfBjV4ViE0zJOMB6NcEvYpy2jNR/8rwRoUGsFPq8//AkAklw18RJyJuqFugsUzPznQvad0IuNJV7jnsmJqo6ur6NUvef6NA7ugUalNv9+imINjChO8HRLRQfRGk6B0D/P3AkBt54UBMtFefOLXgUdilwLdCUSw4KpbuBPw+cyWlMjcXCkj4rHoeksekyBH1GrBJkLqDMRqtVQUubuFwSzBAtlc";
//票通公钥(票通提供)
private static String ptPublicKey =
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJkx3HelhEm/U7jOCor29oHsIjCMSTyKbX5rpoAY8KDIs9mmr5Y9r+jvNJH8pK3u5gNnvleT6rQgJQW1mk0zHuPO00vy62tSA53fkSjtM+n0oC1Fkm4DRFd5qJgoP7uFQHR5OEffMjy2qIuxChY4Au0kq+6RruEgIttb7wUxy8TwIDAQAB";
//3DES秘钥(票通提供)
private static String password = "lsBnINDxtct8HZB7KCMyhWSJ";
//请更换请求平台简称(票通提供)
private static String platform_alias = "DEMK";
//请更换请求平台编码(票通提供)
private static String platform_code = "11111111";
public static void Main(string[] args)
{
Console.Write(PublicData.disposeResponse(Blue(), ptPublicKey, password));//蓝票接口
// testqueryInvoice();//查询接口
// testGetInvoiceRepertoryInfo();//库存接口
// testAuthWeChatCards();//插入微信卡包接口
// testTitleInfo();//查询抬头接口
// testGetPTBoxStatus();//查询设备状态
// testGetQrCodeByItems();//获取开票二维码和提取码
// testdeleteInvoiceQrCode();//作为二维码
// testInvoiceRed();//冲红接口
// testRegister();// 注册接口
//string content = "{\"code\":\"0000\",\"msg\":\"处理成功\",\"sign\":\"ZjAqLXwvEEgz2+jzP/+vUWGuvhBr4N4Gg/pLLOt90sMP160SC1RrkOy6b5p1CCx3y4QYRkbqq2NmkYXpAJX5BdkoXFYUO1hF4ufUvYPmIjQvKT9JMnXt1RV0EdNLliiEowJPjjXDSlTZdthIsTXdVirCkGohzLt3b/2YU9moAM8=\",\"serialNo\":\"CTXP20181206100927n5ObjiJM\",\"content\":\"q55jwSlpLhWV7cnEgNTvm+bswSXLiOPDbw8HvqR7SKhQDWJ/x1qlcJHAOB2lYHmQmefePoaVJ4abG7O9aJwIssDZsit2a2pqNeiCWqVmKhceLAsD/IV4DAlHmwZZhb9tqqco+HDHmZlqJy9pQv478OW0UDx/X0kTbIy4au5pZvJdODh4t31o5I2HrGm1HNcykyKMDpr5D1Mx2mYsjHm95OKBAzLPKMo+o1JrotnyjlS08CbbF6CF5OPZPB8tu88g0xl1u7/3kkjgc0KEmE+bQTEF6RoLqtQ9XRdfHf+tjzLpUcfS7j/nzPcHJnU3d1PGU0NsR+QNyHvI2cfo8HLlmnL5V7GDX+iSMNKMJ8vq7lWwcHvxZjyrHRzSpmxsQJXkQN4hungnNjiNGWzJZ8FssSLLkHw3VlQVJ8mz9sugsCn3Gr/muwUG46W7AsxUqM0Oo1JrotnyjlT6yPhLDxoIzumCiet4Hf02Gxfox417aZ6Jw+BGXo/B9KtKAIlQfkV8Zen4leGaPYo+6G+NPE2a7E+g3FRb571HMiwddiHpNVYzpc/pGTxna1JDIOODExKTJPCrI47HGZGbG7O9aJwIssDZsit2a2pqTWa8x1ePRf8eLAsD/IV4DAlHmwZZhb9tqqco+HDHmZlqJy9pQv478OW0UDx/X0kTbIy4au5pZvJdODh4t31o5JaXBuJBVtYBkyKMDpr5D1Mx2mYsjHm95F0rfY1FyxjYo1JrotnyjlS08CbbF6CF5MlEbxEcfrXqIRR3QbB604fgc0KEmE+bQTEF6RoLqtQ9XRdfHf+tjzILJodvesFM/gbYzWVOUKLKkw3+uglxfg0H9K+suDCPJFRGRT6xxFCnTglsJo/q9b0bF+jHjXtpnu9+bNNgN22dfgeGCjXTZ52gwQVlALiNUkaR5cdbKH8gRWCS9TRcrE1pnHLSywkCgr2LCkRS/wEd1863dwA7HMJuY8TDqXWlYC/kkUF84Oo8kyKMDpr5D1NA31vurQ5/BHbrS0QR43dycGeIzhqufLnEBxK01e2CYnK3sqwBwwBa6qhdLFlh9DTb7pjjR5w00A/i74mi2g8Fq91vU5nj5kkoL7fsH3ChjxJwiAQA8RwGPsTnkCcQSnzqj8s+uTYMPFzmWuWd2UYP\"}";
//PublicData.disposeResponse(content,ptPublicKey,password);
}
/**
* 注册接口
*/
public static string testRegister()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/register.pt";
Hashtable map = new Hashtable();
map.Add("taxpayerNum", platform_alias + "0003242300000"); //销方纳税人识别号
map.Add("enterpriseName", "测试C#"); //销方企业名称
map.Add("legalPersonName", "AA"); //法人名称
map.Add("contactsName", "AA"); //联系人名称
map.Add("contactsEmail", "1121@qq.com"); //联系人邮箱
map.Add("contactsPhone", "15111111133"); //联系人手机号
map.Add("regionCode", "11"); //地区编码
map.Add("cityName", "海淀区"); //市(区)名
map.Add("enterpriseAddress", "地址"); //详细地址
// TODO 请修改为正确的图片Base64传
map.Add("taxRegistrationCertificate", "sdddddddddddddddddddd"); //证件图片base64
string content = ToJson.Table2Json(map);
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:" + response);
return response;
}
/**
* 开具蓝票
*/
public static string Blue()
{
string url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/invoiceBlue.pt";
ArrayList itemList = new ArrayList();
Hashtable OuterMessage = new Hashtable();
OuterMessage.Add("taxpayerNum", "500102201007206608");
// TODO 请更换请求流水号前缀
OuterMessage.Add("invoiceReqSerialNo", "SCPTT538117842484711");
OuterMessage.Add("buyerName", "购买购买方名称购买方名称购买方名称");
OuterMessage.Add("buyerAddress", "购买方地址");
OuterMessage.Add("buyerTel", "1234-56789104");
OuterMessage.Add("sellerBankAccount", "123456789");
OuterMessage.Add("sellerAddress", "深圳市福田区沙头街道天安社区深南大道车是多少大所大所大多所大所大cdtuiolj");
OuterMessage.Add("sellerTel", "17603327743");
OuterMessage.Add("takerEmail", "767034475@qq.com");
OuterMessage.Add("drawerName", "");
OuterMessage.Add("casherName", "收款人Dd");
OuterMessage.Add("reviewerName", "复核人Bb");
OuterMessage.Add("takerName", "");
OuterMessage.Add("definedData", "测试数据1,测试数据2");
Hashtable InnerMessageOne = new Hashtable();
InnerMessageOne.Add("taxClassificationCode", "1010101020000000000"); //税收分类编码(可以按照Excel文档填写)
InnerMessageOne.Add("quantity", "1.00"); //数量
InnerMessageOne.Add("goodsName", "货物名称"); //货物名称
InnerMessageOne.Add("unitPrice", "5.64"); //单价
InnerMessageOne.Add("invoiceAmount", "5.64"); //金额
InnerMessageOne.Add("taxRateValue", "0.16"); //税率
InnerMessageOne.Add("includeTaxFlag", "0"); //含税标识
Hashtable InnerMessageTwo = new Hashtable();
InnerMessageTwo.Add("taxClassificationCode", "1010101020000000000"); //税收分类编码(可以按照Excel文档填写)
InnerMessageTwo.Add("quantity", "1.00"); //数量
InnerMessageTwo.Add("goodsName", "货物名称"); //货物名称
InnerMessageTwo.Add("unitPrice", "5.64"); //单价
InnerMessageTwo.Add("invoiceAmount", "5.64"); //金额
InnerMessageTwo.Add("taxRateValue", "0.16"); //税率
InnerMessageTwo.Add("includeTaxFlag", "0"); //含税标识
itemList.Add(InnerMessageOne);
itemList.Add(InnerMessageTwo);
OuterMessage.Add("itemList", itemList);
string content = ToJson.Table2Json(OuterMessage);
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.WriteLine("最终返回:" + response);
return response;
}
/**
* 红票开具接口
*/
public static void testInvoiceRed()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/invoiceRed.pt";
Hashtable map = new Hashtable();
map.Add("taxpayerNum", "110101201702071"); //销方税号(请于要冲红的蓝票税号一致)
// TODO 请更换请求流水号前缀
map.Add("invoiceReqSerialNo", platform_alias + "5678902275418903"); //发票流水号 (唯一, 与蓝票发票流水号不一致)
map.Add("invoiceCode", "150003529999"); //冲红发票的发票代码
map.Add("invoiceNo", "61033842"); //冲红发票的发票号码
map.Add("redReason", "冲红"); //冲红原因
map.Add("amount", "-65.70"); //冲红金额 (要与原发票的总金额一致)
string content = ToJson.Table2Json(map);
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:" + response);
}
/**
* 开票二维码接口
*/
public static void testGetQrCodeByItems() {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getQrCodeByItems.pt";
Hashtable map = new Hashtable();
map.Add("taxpayerNum", "110101201705230001"); //销方纳税人识别号
map.Add("enterpriseName", "测试"); //销方企业名称
map.Add("tradeNo", platform_alias + "10002001");//订单号(唯一)
map.Add("tradeTime", "2017-06-26 09:15:54"); //交易时间
map.Add("invoiceAmount", "100"); //发票金额(含税)
map.Add("casherName", "收款人A"); //收款人姓名(校验规则: 中文/字母大小写/及其两者组合)
map.Add("reviewerName", "审核人A"); //审核人姓名(校验规则: 中文/字母大小写/及其两者组合)
map.Add("drawerName", "开票人A"); //开票人姓名(校验规则: 中文/字母大小写/及其两者组合)
map.Add("allowInvoiceCount", "1"); //允许开票张数(非必填 默认值:1)
// map.put("smsFlag", "false"); //是否发送短信 (非必填 默认值:false 测试环境不发送短信)
// map.put("expireTime", ""); //有效时间 (非必填 默认值:永久有效 填写格式 yyyy-MM-dd HH:mm:ss)
// map.put("email","XXXXX@XX.com"); //二维码发送邮箱地址(非必填)
//其他参数见接口文档
ArrayList list = new ArrayList();
Hashtable listMapOne = new Hashtable();
listMapOne.Add("itemName", "小麦"); //开票项目名
listMapOne.Add("taxRateValue", "0.16"); //税率
listMapOne.Add("taxClassificationCode", "1010101020000000000");//税收分类编码
listMapOne.Add("quantity", "1"); //数量
listMapOne.Add("unitPrice", "50"); //单价
listMapOne.Add("invoiceItemAmount", "50"); //金额
Hashtable listMapTwo = new Hashtable();
listMapTwo.Add("itemName", "大米");
listMapTwo.Add("taxRateValue", "0.16");
listMapTwo.Add("taxClassificationCode", "1010101020000000000");
listMapTwo.Add("quantity", "1");
listMapTwo.Add("unitPrice", "50");
listMapTwo.Add("invoiceItemAmount", "50");
list.Add(listMapOne);
list.Add(listMapTwo);
map.Add("itemList", list);
string content = ToJson.Table2Json(map);
String builderrequest=PublicData.publicparam(content,platform_code,platform_alias,privateKey,password);
string response= PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:"+response);
}
/**
* 作废二维码接口
*/
public static void testdeleteInvoiceQrCode()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/deleteInvoiceQrCode.pt";
string content = "[{\"taxpayerNum\":\"110101201705230001\",\"enterpriseName\":\"测试\",\"tradeNo\":\"DEMO10002001\",\"tradeTime\":\"2017-06-26 09:15:54\",\"invoiceAmount\":\"100\"}]";
String builderrequest=PublicData.publicparam(content,platform_code,platform_alias,privateKey,password);
string response= PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:"+response);
}
/**
* 查询发票
*/
public static void testqueryInvoice()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryInvoice.pt";
// String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryInvoiceInfo.pt"; //查询发票票面全面信息地址
String content =
"{ \"taxpayerNum\": \"110101201702071\", \"invoiceReqSerialNo\": \"DEMO6678997514279636\"}";
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:" + response);
}
/*
* 获取库存接口
*/
public static void testGetInvoiceRepertoryInfo()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getInvoiceRepertoryInfo.pt";
String content = "{\"taxpayerNum\":\"110101201702071\",\"enterpriseName\":\"电子票测试新1\"}";
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:" + response);
}
/**
* 插入微信卡包接口
*/
public static void testAuthWeChatCards()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/authWeChatCards.pt";
String content = "{\"taxpayerNum\":\"110101201705230001\",\"invoiceReqSerialNo\":\"GAGA0000000000000009\"}";
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:" + response);
}
/**
* 查询发票抬头
*/
public static void testTitleInfo()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getInvoiceTitleInfo.pt";
String content = "{\"enterpriseName\":\"测试\"}";
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:" + response);
}
/**
* 查询票通宝状态接口
*/
public static void testGetPTBoxStatus()
{
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getPTBoxStatus.pt";
String content = "{\"taxpayerNum\":\"110101201702071\",\"enterpriseName\":\"电子票测试新1\"}";
String builderrequest =
PublicData.publicparam(content, platform_code, platform_alias, privateKey, password);
string response = PostJson.Post4Json(url, builderrequest);
Console.Write("最终返回:" + response);
}
}
}
@@ -0,0 +1,57 @@
using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Text;
namespace ConsoleDemo
{
internal class ToJson
{
public static String Table2Json(Hashtable table)
{
StringBuilder jsonstr =new StringBuilder();
jsonstr.Append("{");
foreach (DictionaryEntry tableEntry in table)
{
if (tableEntry.Key=="itemList")
{
String liststr=list2json((ArrayList)tableEntry.Value);
jsonstr.Append(string.Format("\"{0}\":{1},", tableEntry.Key,liststr));
}
else
{
jsonstr.Append(string.Format("\"{0}\":\"{1}\",", tableEntry.Key, tableEntry.Value));
}
}
jsonstr.Append("}");
jsonstr.Remove(jsonstr.Length - 2, 1);
return jsonstr.ToString();
// Console.Write(jsonstr.ToString());
}
private static String list2json(ArrayList list)
{
StringBuilder jsonstr =new StringBuilder();
jsonstr.Append("[");
foreach (Hashtable valuelist in list)
{
jsonstr.Append(Table2Json(valuelist)) ;
jsonstr.Append(",");
}
jsonstr.Remove(jsonstr.Length - 1, 1);
jsonstr.Append("]");
return jsonstr.ToString();
}
}
}
@@ -0,0 +1,831 @@
package org.example;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.junit.jupiter.api.Test;
import java.io.UnsupportedEncodingException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 票通Demo 用于参考报文组装和加密示例
*
* HtppUtils :post请求用的java原生的IO.net,没有考虑超时/长连接等特殊情况.使用到真正项目时最好采用项目http框架
*
* demo在json转换的时候使用了Gson,单元测试的时候使用了Junit 以下是Maven依赖有需要添加即可:
* <dependency>
* <groupId>com.google.code.gson</groupId>
* <artifactId>gson</artifactId>
* <version>2.8.9</version>
* </dependency>
*
*
* <dependency>
* <groupId>junit</groupId>
* <artifactId>junit</artifactId>
* <version>4.13.2</version>
* <scope>test</scope>
* </dependency>
*
*/
public class Demo {
//私钥(与发给票通的公钥为一对)
private String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIVLAoolDaE7m5oMB1ZrILHkMXMF6qmC8I/FCejz4hwBcj59H3rbtcycBEmExOJTGwexFkNgRakhqM+3uP3VybWu1GBYNmqVzggWKKzThul9VPE3+OTMlxeG4H63RsCO1//J0MoUavXMMkL3txkZBO5EtTqek182eePOV8fC3ZxpAgMBAAECgYBp4Gg3BTGrZaa2mWFmspd41lK1E/kPBrRA7vltMfPj3P47RrYvp7/js/Xv0+d0AyFQXcjaYelTbCokPMJT1nJumb2A/Cqy3yGKX3Z6QibvByBlCKK29lZkw8WVRGFIzCIXhGKdqukXf8RyqfhInqHpZ9AoY2W60bbSP6EXj/rhNQJBAL76SmpQOrnCI8Xu75di0eXBN/bE9tKsf7AgMkpFRhaU8VLbvd27U9vRWqtu67RY3sOeRMh38JZBwAIS8tp5hgcCQQCyrOS6vfXIUxKoWyvGyMyhqoLsiAdnxBKHh8tMINo0ioCbU+jc2dgPDipL0ym5nhvg5fCXZC2rvkKUltLEqq4PAkAqBf9b932EpKCkjFgyUq9nRCYhaeP6JbUPN3Z5e1bZ3zpfBjV4ViE0zJOMB6NcEvYpy2jNR/8rwRoUGsFPq8//AkAklw18RJyJuqFugsUzPznQvad0IuNJV7jnsmJqo6ur6NUvef6NA7ugUalNv9+imINjChO8HRLRQfRGk6B0D/P3AkBt54UBMtFefOLXgUdilwLdCUSw4KpbuBPw+cyWlMjcXCkj4rHoeksekyBH1GrBJkLqDMRqtVQUubuFwSzBAtlc";
//票通公钥(票通提供)
private String ptPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJkx3HelhEm/U7jOCor29oHsIjCMSTyKbX5rpoAY8KDIs9mmr5Y9r+jvNJH8pK3u5gNnvleT6rQgJQW1mk0zHuPO00vy62tSA53fkSjtM+n0oC1Fkm4DRFd5qJgoP7uFQHR5OEffMjy2qIuxChY4Au0kq+6RruEgIttb7wUxy8TwIDAQAB";
//3DES秘钥(票通提供)
private final static String password = "lsBnINDxtct8HZB7KCMyhWSJ";
//请更换请求平台简称(票通提供)
private final static String platform_alias = "DEMK";
//请更换请求平台编码(票通提供)
private final static String platform_code = "11111111";
//销售方税号(测试环境票通提供,正式环境使用正式税号)
private final static String taxpayerNum = "500102201007206608";
/**
* @throws Exception
* @title: testRSAGenerate
* @description: RAS公钥私钥的生成 1024bit pkcs8格式 公钥提供给票通 私钥保留
*/
@Test
public void testRSAGenerate() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String publicKeyStr = RSAUtil.getKeyString(publicKey);
System.out.println("publicKeyString:" + publicKeyStr);
String privateKeyStr = RSAUtil.getKeyString(privateKey);
System.out.println("privateKeyString:" + privateKeyStr);
}
/**
* @title: testRealEstateRentalInvoiceBlue
* @description: 蓝票接口调用
*/
@Test
public void testInvoiceBlue() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/invoiceBlue.pt";
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum); //销方税号
map.put("invoiceReqSerialNo", date(platform_alias));//发票请求流水号
map.put("buyerName", "购买方名称");//购买方名称
map.put("invoiceIssueKindCode", "82");//购买方名称
map.put("buyerTaxpayerNum", "XX0000000000000000");//购买方税号(非必填,个人发票传null)
map.put("remark", "扫码");//购买方税号(非必填,个人发票传null)
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Map<String, String> listMapOne = new HashMap<String, String>();
listMapOne.put("taxClassificationCode", "3040101000000000000");//税收分类编码(可以按照Excel文档填写)
listMapOne.put("quantity", "1.00");//数量
listMapOne.put("goodsName", "研发服务");//货物名称
listMapOne.put("unitPrice", "10");//单价
listMapOne.put("invoiceIssueKindCode", "82");//发票种类 (注意:数电发票在81与82之间选择 )81:数电票(增值税专用发票)82:数电票(普通发票)10:增值税电子普通发票
listMapOne.put("invoiceAmount", "10");//金额
listMapOne.put("taxRateValue", "0.13");//税率(注:金税三期之后 不存在16% 与10%税率 16%自动会降为13% 10%自动降为9%) 如果使用一般人使用3% 或 5%税率请与财务确认是否享受了优惠政策
listMapOne.put("includeTaxFlag", "0");//含税标识
listMapOne.put("account", null);//数电账号,传入字段会指定开票员进行开票,不传则随机取一名开票员进行开票 (注: 开票员需要实现在票通登记)
/**
* 以下为零税率开票相关参数
* 零税商品为特殊税率,正式环境使用零税要事先与相关财务人员沟通该企业是否有零税商品,且适用那种零税状态
* (免税,不征税,普通零税率)其中一种
*/
zeroGoodsformat(null, listMapOne);//零税商品类型 1:免税,2:不征税,3:普通零税率,4:简易征收,5:按5%简易征收.其它:非零税率,
list.add(listMapOne);
map.put("itemList", list);
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
}
/**
* @title: testRealEstateRentalInvoiceBlue
* @description: 蓝票接口调用(开具不动产租赁发票) 非特定业务无视即可. 停车费属于特定业务
*
*/
@Test
public void testRealEstateRentalInvoiceBlue() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/invoiceBlue.pt";
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum); //销方税号
map.put("invoiceReqSerialNo", date(platform_alias));//发票请求流水号
map.put("buyerName", "购买方名称");//购买方名称
map.put("invoiceIssueKindCode", "82");//购买方名称
map.put("buyerTaxpayerNum", "XX0000000000000000");//购买方税号(非必填,个人发票传null)
map.put("remark", "扫码");//购买方税号(非必填,个人发票传null)
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Map<String, String> listMapOne = new HashMap<String, String>();
listMapOne.put("taxClassificationCode", "3040502020200000000");//税收分类编码(可以按照Excel文档填写)
listMapOne.put("quantity", "1.00");//数量
listMapOne.put("goodsName", "*货物*货物名称");//货物名称
listMapOne.put("unitPrice", "10");//单价
listMapOne.put("invoiceIssueKindCode", "82");//发票种类 (注意:数电发票在81与82之间选择 )81:数电票(增值税专用发票)82:数电票(普通发票)10:增值税电子普通发票
listMapOne.put("invoiceAmount", "10");//金额
listMapOne.put("taxRateValue", "0.13");//税率(注:金税三期之后 不存在16% 与10%税率 16%自动会降为13% 10%自动降为9%)
listMapOne.put("includeTaxFlag", "0");//含税标识
listMapOne.put("account", null);//数电账号,传入字段会指定开票员进行开票,不传则随机取一名开票员进行开票 (注: 开票员需要实现在票通登记)
/**
* 以下为零税率开票相关参数
* 零税商品为特殊税率,正式环境使用零税要事先与相关财务人员沟通该企业是否有零税商品,且适用那种零税状态
* (出口零税率,免税,不征税,普通零税率)其中一种
*/
zeroGoodsformat(null, listMapOne);//零税商品类型 0:出口零税率,1:免税,2:不征税,3:普通零税率,4:简易征收,5:按5%简易征收.其它:非零税率,
Map<String, String> realEstateRentalService = new HashMap<String, String>();
realEstateRentalService.put("region", "四川省绵阳市涪城区");
realEstateRentalService.put("detailedAddress", "东江北路 68 号");
realEstateRentalService.put("areaUnit", "平方米");
realEstateRentalService.put("crossCitySign", "0");
realEstateRentalService.put("leaseTerm", "2022-12-01 12:10 2022-12-12 15:00");
realEstateRentalService.put("titleNo", "");
realEstateRentalService.put("carPlateNum", "京A4564651");
map.put("realEstateRentalService", realEstateRentalService);//购买方税号(非必填,个人发票传null)
list.add(listMapOne);
map.put("itemList", list);
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
}
/**
* @title: testRegister
* @description: 注册接口调用
*/
@Test
public void testRegister() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/register.pt";
Map<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", date(platform_alias));//销方纳税人识别号 测试注册时也请使用贵公司的企业税号进行注册
map.put("enterpriseName", "票通信息");//销方企业名称
map.put("legalPersonName", "AA");//法人名称
map.put("contactsName", "AA");//联系人名称
map.put("contactsEmail", "1121@qq.com");//联系人邮箱
map.put("contactsPhone", "15111111133");//联系人手机号
map.put("regionCode", "11");//地区编码
map.put("cityName", "海淀区");//市(区)名
map.put("enterpriseAddress", "地址");//详细地址
// TODO 请修改为正确的图片Base64传
map.put("taxRegistrationCertificate", "sdddddddddddddddddddd");//证件图片base64
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
String responseI = this.disposeResponse(response, ptPublicKey, password);
System.out.println(response);
System.out.println(responseI);
}
/**
* @title: testInvoiceRed
* @description: 红票接口调用
*/
@Test
public void testInvoiceRed() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/invoiceRed.pt";
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum);//销方税号(请于要冲红的蓝票税号一致)
// TODO 请更换请求流水号前缀
map.put("invoiceReqSerialNo", platform_alias + "5678902234568904");//发票流水号 (唯一, 与蓝票发票流水号不一致)
//(invoiceCode和invoiceNo) 与 (blueAllEleInvNo) 只能传一个
map.put("invoiceCode", "");//冲红发票的发票代码 原蓝票为税控发票时传递
map.put("invoiceNo", "");//冲红发票的发票号码 原蓝票为税控发票时传递
map.put("blueAllEleInvNo", "25019200000097980167");//冲红发票的发票号码 原蓝票为数电发票时传递
map.put("amount", "-11.30");//冲红金额 (要与原发票的总金额一致)
// map.put("blueInvoiceDate", "20250101");//蓝票开票日期.冲红的原蓝票非票通开具的发票时候使用
map.put("redReason", "01");//冲红原因冲红原因,不传默认 01 01:开票有误 02:销货退回 03:服务中止 04:销售折让
map.put("invoiceKind", null);//发票种类 用于声明开具的红票是什么发票 不传跟随原蓝票的发票种类一致 (注意:数电发票在81与82之间选择 )81:数电票(增值税专用发票)82:数电票(普通发票)10:增值税电子普通发票
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
}
/**
* @title: testAuthWeChatCards
* @description:查询发票
*/
@Test
public void testqueryInvoice() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryInvoice.pt";
// String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryInvoiceInfo.pt"; //查询发票票面全面信息地址
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum);//销方税号(要跟被查询的发票税号一致)
// TODO 请更换请求流水号前缀
map.put("invoiceReqSerialNo", platform_alias + "2025021811251811");//发票流水号 (查询的红票或者蓝票开具时所填写的invoiceReqSerialNo一致)
Gson gson = new Gson();
String content = gson.toJson(map);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
@Test
public void testgetUnLoginUrl() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getUnLoginUrl.pt";
// String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryInvoiceInfo.pt"; //查询发票票面全面信息地址
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum);//销方税号(要跟被查询的发票税号一致)
// TODO 请更换请求流水号前缀
map.put("redirectUrl", "jtgl.piaotong.vip");//发票流水号 (查询的红票或者蓝票开具时所填写的invoiceReqSerialNo一致)
map.put("customData", "jtgl.piaotong.vip");
Gson gson = new Gson();
String content = gson.toJson(map);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
@Test
public void testgetUnAuthUrl() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getUnAuthUrl.pt";
// String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryInvoiceInfo.pt"; //查询发票票面全面信息地址
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum);//销方税号(要跟被查询的发票税号一致)
// TODO 请更换请求流水号前缀
map.put("token", "9297dbfcd1ff4b179d56406453a3c9f9");//发票流水号 (查询的红票或者蓝票开具时所填写的invoiceReqSerialNo一致)
// map.put("customData", "jtgl.piaotong.vip");
Gson gson = new Gson();
String content = gson.toJson(map);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: testGetInvoiceQrAndExtractCode
* @description: 获取多项目开票二维码和提取码接口
*/
@Test
public void testGetInvoiceIssueQrcodeItems() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getInvoiceIssueQrcode.pt";
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum); //销方纳税人识别号
map.put("enterpriseName", taxpayerNum); //销方企业名称 如实填写 测试环境环境提供的测试税号名称与税号恰好一致. 正式环境不要模仿
map.put("qrcodeNo", date(platform_alias));//二维码编号(唯一)
map.put("tradeNo", "DEMO12345678900");//订单号
map.put("tradeTime", "2017-06-26 09:15:54"); //交易时间
map.put("invoiceAmount", "3"); //发票金额(含税)
map.put("casherName", "收款人A"); //收款人姓名(校验规则: 中文/字母大小写/及其两者组合)
map.put("reviewerName", "审核人A"); //审核人姓名(校验规则: 中文/字母大小写/及其两者组合)
map.put("drawerName", "开票人A"); //开票人姓名(校验规则: 中文/字母大小写/及其两者组合)
map.put("allowInvoiceCount", "1"); //允许开票张数(非必填 默认值:1)
map.put("definedData", "所填软件");
// map.put("smsFlag", "false"); //是否发送短信 (非必填 默认值:false 测试环境不发送短信)
// map.put("expireTime", ""); //有效时间 (非必填 默认值:永久有效 填写格式 yyyy-MM-dd HH:mm:ss)
// map.put("email","XXXXX@XX.com"); //二维码发送邮箱地址(非必填)
//其他参数见接口文档
//可选发票种类及分机列表信息
List<Map<String, String>> invoiceIssueOptionsList = new ArrayList<Map<String, String>>();
Map<String, String> invoiceIssueOptionsMap = new HashMap<String, String>();
invoiceIssueOptionsMap.put("invoiceType", "82");//可选发票种类 10税控电普 08税控电专 82数电普票 81数电纸票
// invoiceIssueOptionsMap.put("extensionNum","0");//分机号
// invoiceIssueOptionsMap.put("machineCode",null);//机器编码
invoiceIssueOptionsList.add(invoiceIssueOptionsMap);
map.put("invoiceIssueOptions", invoiceIssueOptionsList);
//商品行
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Map<String, String> listMapOne = new HashMap<String, String>();
listMapOne.put("itemName", "小麦"); //开票项目名
listMapOne.put("taxRateValue", "0.13"); //税率(注:金税三期之后 不存在16% 与10%税率 16%自动会降为13% 10%自动降为9%)
listMapOne.put("taxClassificationCode", "1010101020000000000");//税收分类编码
listMapOne.put("quantity", "1"); //数量
listMapOne.put("unitPrice", "2"); //单价
listMapOne.put("invoiceItemAmount", "2"); //金额
Map<String, String> listMapTwo = new HashMap<String, String>();
listMapTwo.put("itemName", "大米");
listMapTwo.put("taxRateValue", "0.13");
listMapTwo.put("taxClassificationCode", "1010101020000000000");
listMapTwo.put("quantity", "1");
listMapTwo.put("unitPrice", "1");
listMapTwo.put("invoiceItemAmount", "1");
list.add(listMapOne);
list.add(listMapTwo);
map.put("itemList", list);
Gson gson = new Gson();
String content = gson.toJson(map);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: queryInvoiceQrCode
* @description:查询二维码信息
*/
@Test
public void queryInvoiceQrCode() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryInvoiceQrCode.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("tradeNo", "DEMO2019090916264942");//订单号
map.put("invoiceAmount", "3");//金额
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: testdeleteInvoiceQrCode
* @description: 作废二维码接口
*/
@Test
public void testdeleteInvoiceQrCode() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/deleteInvoiceQrCode.pt";
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
Map<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum); //销方税号
map.put("enterpriseName", taxpayerNum);//企业名称 如实填写 测试环境环境提供的测试税号名称与税号恰好一致. 正式环境不要模仿
map.put("tradeNo", "CTXP2018091910241715");//与开票二维码的订单号一致
map.put("tradeTime", "2017-06-26 09:15:54"); //与开票二维码的时间一致
map.put("invoiceAmount", "1.00");//金额一致
list.add(map);
Gson gson = new Gson();
String content = gson.toJson(map);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: testAuthWeChatCards
* @description:查询发票抬头信息
*/
@Test
public void testTitleInfo() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getInvoiceTitleInfo.pt";
String content = "{\"enterpriseName\":\"北京票通\"}";
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
System.out.println(buildRequest);
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: testAuthWeChatCards
* @description:微信卡包授
*/
@Test
public void testAuthWeChatCards() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/authWeChatCards.pt";
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum);//销方税号(要跟被查询的发票税号一致)
// TODO 请更换请求流水号前缀
map.put("invoiceReqSerialNo", platform_alias + "2025021811251811");//发票流水号 (查询的红票或者蓝票开具时所填写的invoiceReqSerialNo一致)
Gson gson = new Gson();
String content = gson.toJson(map);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: queryInvoiceQrCode
* @description:重发短信接口
*/
@Test
public void resendEmailOrSMS() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/resendEmailOrSMS.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("invoiceReqSerialNo", "DEMO2019090916264942");//流水号 注:发票请求流水号和发票代码号码不能同时为空,如果都填写以发票请求流水号为准
map.put("invoiceCode", "3");//发票代码
map.put("invoiceNo", "3");//发票号码
map.put("takerEmail", "3");// 收票人邮箱,不传的话默认使用开具时传的收票人邮箱
map.put("takerTel", "3");// 收票人手机号,无默认,需要校验企业是否开通发送短信。
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
*
*
* 以下为数电专用接口
*
*/
/**
* @title: testregisterUser
* @description:用户登记接口
*/
@Test
public void testregisterUser() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/registerUser.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("loginMethod", "1");//登录方式。1:用户名(居民身份证号码/手机号码/用户名)+密码 只有1
map.put("account", "DEMOadmin");//电子税局登录账号(手机号或身份证号),若平台已经存在做更新操作
map.put("password", SecurityUtil.encrypt3DES(password, "ispassword"));//电子税局登录密码 3DES 加密
map.put("identityType", "01");// 登录身份类型. 要与电子税局身份一致 01:法定代表人02:财务负责人03:办税员 04:涉税服务人员05:管理员07:领票人09:开票员99:其他人员
map.put("identityPwd", null);// 登录身份密码 (只有部分地区需要.目前多数区域不需要,预留不传值)
map.put("phoneNum", "13000000000");// 手机号码。当前登记用户的手机号。如果是手机号+密码登录,该值必须和 account 一致
map.put("name", "测试");//姓名 最好要跟登记的数电账户对应的开票人一致
map.put("operationType", null);// 操作类型。默认 1 登记。 1:登记;2:删除 多数情况下不传
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: sendLoginSmsCode
* @description:获取登录短信验证码
*/
@Test
public void sendLoginSmsCode() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/sendLoginSmsCode.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("account", "DEMOadmin");//电子税局登录账号(手机号或身份证号),若平台已经存在做更新操作
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: smsLogin
* @description:短信登录
*/
@Test
public void smsLogin() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/smsLogin.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("account", "DEMOadmin");//电子税局登录账号(手机号或身份证号),若平台已经存在做更新操作
map.put("smsCode", "123456");//短信验证码
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: getAuthenticationQrcode
* @description:获取实名认证二维码
*/
@Test
public void getAuthenticationQrcode() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getAuthenticationQrcode.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("account", "DEMOadmin");//电子税局登录账号(手机号或身份证号),若平台已经存在做更新操作
map.put("qrcodeType", "1");//二维码类型 1:税务 APP; 2:个人所得税 APP .不传值默认 1 税务 APP。
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: queryAuthQrcodeScanStatus
* @description:查询实名认证二维码扫码状态
*/
@Test
public void queryAuthQrcodeScanStatus() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/queryAuthQrcodeScanStatus.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("account", "DEMOadmin");//电子税局登录账号(手机号或身份证号),若平台已经存在做更新操作
map.put("authId", "1");//认证 id,推送开票结果/查询开票结果/获取实名认证二维码时会随二维码一起返回
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: getTaxBureauAccountAuthStatus
* @description:查询数电账号认证状态
*/
@Test
public void getTaxBureauAccountAuthStatus() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getTaxBureauAccountAuthStatus.pt";
HashMap<String, String> map = new HashMap<String, String>();
map.put("taxpayerNum", taxpayerNum);//销方税号
map.put("account", "DEMOadmin");//电子税局登录账号(手机号或身份证号),若平台已经存在做更新操作
Gson gson = new Gson();
String content = gson.toJson(map);
System.out.println(content);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
*
* 以下为税控专用接口
*
*/
/**
* @title: testGetPTBoxStatus
* @description: 获取票通宝状态接口
*/
@Test
public void testGetPTBoxStatus() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getPTBoxStatus.pt";
String content = "{\"taxpayerNum\":\"110105201606160003\",\"enterpriseName\":\"110105201606160003\"}";
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: testGetInvoiceRepertoryInfo
* @description: 获取库存接口
*/
@Test
public void testGetInvoiceRepertoryInfo() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/getInvoiceRepertoryInfo.pt";
Map<String, Object> map = new HashMap<String, Object>();
map.put("taxpayerNum", taxpayerNum);//销方税号(要跟被查询的发票税号一致)
// TODO 请更换请求流水号前缀
map.put("enterpriseName", taxpayerNum);//企业名称 如实填写 测试环境环境提供的测试税号名称与税号恰好一致. 正式环境不要模仿
Gson gson = new Gson();
String content = gson.toJson(map);
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
* @title: revokeInvoiceIssue
* @description:撤销开票
*/
@Test
public void revokeInvoiceIssue() throws Exception {
String url = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/revokeInvoiceIssue.pt";
String content = "{\"taxpayerNum\":\"110105201606160003\",\"invoiceReqSerialNo\":\"DEMO2019090916515155\"}";
//OpenApi参数内容(3des秘钥(票通提供),平台编码(票通提供),平台前缀(票通提供),私钥)
String buildRequest = this.buildRequestData(platform_code, platform_alias, content, password, privateKey);
;
String response = HttpUtils.postJson(url, buildRequest);
System.out.println(response);
System.out
.println(this.disposeResponse(response, ptPublicKey, password));
}
/**
*
* 以下为便捷工具 ,非调用接口
*/
/**
* @title: test3DES
* @description: 3DES加密
*/
@Test
public void test3DES() throws Exception {
String content = "{\"taxpayerNum\": \"9120931023801231\",\"enterpriseName\": \"西单大悦城有限公司\",\"paymentTransID\": \"12109238102831023102983\",\"paymentType\": \"2\",\"paymentTransTime\": \"2017-01-19 18:20:09\",\"paymentTransMoney\": \"20\",\"orderID\": \"12109238102831023102981\",\"orderMoney\": \"30\"}";
System.out.println(SecurityUtil.encrypt3DES(password, content));
}
/**
* @title: test3DESDecry
* @description: 3DES解密
*/
@Test
public void test3DESDecry() throws Exception {
String str = "WkoTqkd08kNxEFqY6bTa/LVHoAj8nYPWJrX8KmI4rrFmJWXMlk5ik7QzwNTvN1Yiq5sGyS17ShQk6UdhwH5XftxVY9W3ytRZr35bic05cZBlq6VejY2AH9Ql5zZu/4xipBD1jTT/6CBeFU5ViYDbGChpDYf8hEVO4JQQl/H5a1SkwtEaPKT8BCQAvy2Sn0ffmCc0NPjaFWASk2bkqM1NzCqFt6BXUjao34IWG2IzUl9O/VXYFAItC/c67lLXu0ziTTK2n0FGLABED5V9uHvhNCvALC81PH1Fd+3KT11i1szg/F79DbzQOK8WrdSnsUSPbyPFC5kA4MbS1xuqEOHsgSBhw0/xPjpq4ODQwPRwjRI=\n";
System.out.println(SecurityUtil.decrypt3DES(password, str));
}
/**
* @title: testBase64
* @description: Base64编码
*/
@Test
public void testBase64() throws Exception {
String str = "JJON0d93C9nQN013N+cCwwIYbRVYlWChGQkSgAWG8g4mD1xFU6oGPauqih5gW7ZTcpejSPS8TqRbdBFdBATSXdwZqPM0q8sVYf3xwlp8OEw6INcUCvRW7myiFkzSJLV4Ost42d5Xp+sicgMj0bn99BsRSqe06BMvYTA46L/vGGPqN4tuuy2B/enpkGLcOQdPdtC+wG8ub6+zykisJT5I7EMls73cjaSlj1iRw/PT9huULu97iPHIiqnKhK05AXkvgWMcfg42+bLeG/kPgbaAtwAkXN/yDkKACcDML2WE8TZ+BFsaQPbH+BfY/XQ4VXSYF5NGeulhDJr1DLIHgH+KNQ==";
System.out.println(encode2String(str));
}
/**
* @title: testBase64
* @description: Base64解码
*/
@Test
public void testBase64toURL() throws Exception {
String str = "SkpPTjBkOTNDOW5RTjAxM04rY0N3d0lZYlJWWWxXQ2hHUWtTZ0FXRzhnNG1EMXhGVTZvR1BhdXFpaDVnVzdaVGNwZWpTUFM4VHFSYmRCRmRCQVRTWGR3WnFQTTBxOHNWWWYzeHdscDhPRXc2SU5jVUN2Ulc3bXlpRmt6U0pMVjRPc3Q0MmQ1WHArc2ljZ01qMGJuOTlCc1JTcWUwNkJNdllUQTQ2TC92R0dQcU40dHV1eTJCL2VucGtHTGNPUWRQZHRDK3dHOHViNit6eWtpc0pUNUk3RU1sczczY2phU2xqMWlSdy9QVDlodVVMdTk3aVBISWlxbktoSzA1QVhrdmdXTWNmZzQyK2JMZUcva1BnYmFBdHdBa1hOL3lEa0tBQ2NETUwyV0U4VForQkZzYVFQYkgrQmZZL1hRNFZYU1lGNU5HZXVsaERKcjFETElIZ0grS05RPT0=";
System.out.println(decode2String(str));
}
public String decode2String(String targetString) throws UnsupportedEncodingException {
byte[] decodedBytes = Base64.getDecoder().decode(targetString);
String decodedString = new String(decodedBytes);
return decodedString;
}
public String encode2String(String targetString) throws UnsupportedEncodingException {
return Base64.getEncoder().encodeToString(targetString.getBytes());
}
/**
* @param s 零税商品类型 0:出口零税率,1:免税,2:不征税,3:普通零税率,4:简易征收,5:按5%简易征收.其它:非零税率,
* @param listMapOne
*/
private void zeroGoodsformat(String s, Map<String, String> listMapOne) {
if ("1".equals(s)) {
listMapOne.put("taxRateValue", "0");
listMapOne.put("zeroTaxFlag", "1");//零税率标识(空:非零税率,0:出口零税率,1:免税,2:不征税,3:普通零税率)
listMapOne.put("preferentialPolicyFlag", "1");//优惠政策标识(空:不使用,1:使用) 注:零税率标识传非空 此字段必须填写为"1"
listMapOne.put("vatSpecialManage", "免税");//增值税特殊管理(preferentialPolicyFlag为1 此参数必填)
} else if ("0".equals(s)) {
listMapOne.put("taxRateValue", "0");
listMapOne.put("zeroTaxFlag", "0");//零税率标识(空:非零税率,0:出口零税率,1:免税,2:不征税,3:普通零税率)
listMapOne.put("preferentialPolicyFlag", "1");//优惠政策标识(空:不使用,1:使用) 注:零税率标识传非空 此字段必须填写为"1"
listMapOne.put("vatSpecialManage", "出口零税");//增值税特殊管理(preferentialPolicyFlag为1 此参数必填)
} else if ("2".equals(s)) {
listMapOne.put("taxRateValue", "0");
listMapOne.put("zeroTaxFlag", "2");//零税率标识(空:非零税率,0:出口零税率,1:免税,2:不征税,3:普通零税率)
listMapOne.put("preferentialPolicyFlag", "1");//优惠政策标识(空:不使用,1:使用) 注:零税率标识传非空 此字段必须填写为"1"
listMapOne.put("vatSpecialManage", "不征税");//增值税特殊管理(preferentialPolicyFlag为1 此参数必填)
} else if ("3".equals(s)) {
listMapOne.put("taxRateValue", "0");
listMapOne.put("zeroTaxFlag", "3");//零税率标识(空:非零税率,0:出口零税率,1:免税,2:不征税,3:普通零税率)
listMapOne.put("preferentialPolicyFlag", null);//优惠政策标识(空:不使用,1:使用) 注:零税率标识传非空 此字段必须填写为"1"
listMapOne.put("vatSpecialManage", null);//增值税特殊管理(preferentialPolicyFlag为1 此参数必填)
} else if ("4".equals(s)) {
listMapOne.put("preferentialPolicyFlag", "1");//优惠政策标识(空:不使用,1:使用) 注:零税率标识传非空 此字段必须填写为"1"
listMapOne.put("vatSpecialManage", "简易征收");//增值税特殊管理(preferentialPolicyFlag为1 此参数必填)
} else if ("5".equals(s)) {
listMapOne.put("taxRateValue", "0.05");
listMapOne.put("preferentialPolicyFlag", "1");//优惠政策标识(空:不使用,1:使用) 注:零税率标识传非空 此字段必须填写为"1"
listMapOne.put("vatSpecialManage", "按5%简易征收");//增值税特殊管理(preferentialPolicyFlag为1 此参数必填)
} else {
listMapOne.put("zeroTaxFlag", null);//零税率标识(空:非零税率,0:出口零税率,1:免税,2:不征税,3:普通零税率)
listMapOne.put("preferentialPolicyFlag", null);//优惠政策标识(空:不使用,1:使用) 注:零税率标识传非空 此字段必须填写为"1"
listMapOne.put("vatSpecialManage", null);//增值税特殊管理(preferentialPolicyFlag为1 此参数必填)
}
}
public String buildRequestData(String platformCode, String prefix, String content, String password, String privateKey) throws Exception {
Map<String, String> map = new HashMap();
String reqContent = SecurityUtil.encrypt3DES(password, content);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
map.put("platformCode", platformCode);
map.put("signType", "RSA");
map.put("format", "JSON");
map.put("version", "1.0");
map.put("content", reqContent);
map.put("timestamp", sdf.format(new Date()));
map.put("serialNo", date(prefix));
map.put("sign", RSAUtil.sign(RSAUtil.getSignatureContent(map), privateKey));
Gson gson = new Gson();
return gson.toJson(map);
}
public String disposeResponse(String jsonStr, String publicKey, String password) {
JsonObject jsonObject = (new JsonParser()).parse(jsonStr).getAsJsonObject();
Gson gson = new Gson();
HashMap map = gson.fromJson(jsonStr, HashMap.class);
String sign = (String) map.remove("sign");
if (RSAUtil.verify(RSAUtil.getSignatureContent(map), sign, publicKey)) {
String plainContent = SecurityUtil.decrypt3DES(password, (String) map.get("content"));
jsonObject.add("content", (new JsonParser()).parse(plainContent));
return jsonObject.toString();
} else {
throw new IllegalStateException("验签失败");
}
}
public String date(String prefix) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("YYYYMMddHHmmss");
String str = prefix + sdf.format(date) + (int) (Math.random() * 90 + 10);
System.out.println(str);
return str;
}
}
@@ -0,0 +1,109 @@
package org.example;
/**
* packageName com.example.demo
*
* @author congtiexin
* @date 2025/6/3
* @description TODO
*/
import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUtils {
private static final Gson gson = new Gson();
/**
* 发送 POST 请求并返回 JSON 响应
*
* @param url 请求地址
* @param request 请求对象(Java Bean 或 Map
* @return 响应内容(Map 或 自定义对象)
*/
public static <T> T postJson(String url, Object request, Class<T> responseType) throws Exception {
HttpURLConnection connection = null;
try {
// 初始化连接
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");
connection.setDoOutput(true);
// 序列化请求体
String jsonInput = gson.toJson(request);
try (OutputStream os = connection.getOutputStream()) {
os.write(jsonInput.getBytes("UTF-8"));
os.flush();
}
// 处理响应
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new RuntimeException("请求失败,状态码: " + responseCode);
}
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"))) {
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
}
// 反序列化响应
return gson.fromJson(response.toString(), responseType);
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
/**
* 快速发送 JSON POST 请求并返回原始字符串响应
*/
public static String postJson(String url, String jsonBody) throws Exception {
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setDoOutput(true);
try (OutputStream os = connection.getOutputStream()) {
byte[] input = jsonBody.getBytes("UTF-8");
os.write(input, 0, input.length);
os.flush();
}
int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new RuntimeException("请求失败,状态码: " + responseCode);
}
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"))) {
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
}
return response.toString();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
@@ -0,0 +1,121 @@
package org.example;
import javax.crypto.Cipher;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
public class RSAUtil {
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
public RSAUtil() {
}
public static String sign(String content, String privateKey) {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initSign(priKey);
signature.update(content.getBytes(DEFAULT_CHARSET));
byte[] signed = signature.sign();
return Base64.getEncoder().encodeToString(signed);
} catch (Exception var7) {
var7.printStackTrace();
return null;
}
}
public static boolean verify(String content, String sign, String publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = Base64.getDecoder().decode(publicKey);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
Signature signature = Signature.getInstance("SHA1WithRSA");
signature.initVerify(pubKey);
signature.update(content.getBytes(DEFAULT_CHARSET));
boolean bverify = signature.verify(Base64.getDecoder().decode(sign));
return bverify;
} catch (Exception var8) {
var8.printStackTrace();
return false;
}
}
public static String encrpyt(String content, String publicKeyStr) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(1, getPublicKey(publicKeyStr));
byte[] enBytes = cipher.doFinal(content.getBytes(DEFAULT_CHARSET));
return Base64.getEncoder().encodeToString(enBytes);
}
public static String decrypt(String content, String privateKeyStr) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(2, getPrivateKey(privateKeyStr));
byte[] deBytes = cipher.doFinal(Base64.getDecoder().decode(content));
return new String(deBytes, DEFAULT_CHARSET);
}
public static PublicKey getPublicKey(String key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}
public static PrivateKey getPrivateKey(String key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
}
public static String getKeyString(Key key) throws Exception {
byte[] keyBytes = key.getEncoded();
return Base64.getEncoder().encodeToString(keyBytes);
}
public static <T> String getSignatureContent(Map<String, T> params) {
if (params == null) {
return null;
} else {
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList(params.keySet());
Collections.sort(keys);
for(int i = 0; i < keys.size(); ++i) {
String key = (String)keys.get(i);
if (params.get(key) != null) {
String value = String.valueOf(params.get(key));
content.append((i == 0 ? "" : "&") + key + "=" + value);
}
}
return content.toString();
}
}
public static String getListSignatureContent(List<Map> mapList) {
if (mapList == null) {
return null;
} else {
List<String> listStr = new ArrayList();
Iterator i$ = mapList.iterator();
while(i$.hasNext()) {
Map<String, Object> map = (Map)i$.next();
listStr.add(getSignatureContent(map));
}
Collections.sort(listStr);
return listStr.toString();
}
}
}
@@ -0,0 +1,67 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.example;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Base64;
public class SecurityUtil {
private static final String ALGORITHM_3DES = "DESede";
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
public SecurityUtil() {
}
public static byte[] encrypt3DES(String encryptPassword, byte[] encryptByte) {
try {
Cipher cipher = init3DES(encryptPassword, 1);
byte[] doFinal = cipher.doFinal(encryptByte);
return doFinal;
} catch (Exception var4) {
return null;
}
}
public static String encrypt3DES(String encryptPassword, String encryptStr) {
try {
Cipher cipher = init3DES(encryptPassword, 1);
byte[] enBytes = cipher.doFinal(encryptStr.getBytes(DEFAULT_CHARSET));
return Base64.getEncoder().encodeToString(enBytes);
} catch (Exception var4) {
return null;
}
}
public static byte[] decrypt3DES(String decryptPassword, byte[] decryptByte) {
try {
Cipher cipher = init3DES(decryptPassword, 2);
byte[] doFinal = cipher.doFinal(decryptByte);
return doFinal;
} catch (Exception var4) {
return null;
}
}
public static String decrypt3DES(String decryptPassword, String decryptString) {
try {
Cipher cipher = init3DES(decryptPassword, 2);
byte[] deBytes = cipher.doFinal(Base64.getDecoder().decode(decryptString));
return new String(deBytes, DEFAULT_CHARSET);
} catch (Exception var4) {
return null;
}
}
private static Cipher init3DES(String decryptPassword, int cipherMode) throws Exception {
SecretKey deskey = new SecretKeySpec(decryptPassword.getBytes(), "DESede");
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(cipherMode, deskey);
return cipher;
}
}
+1
View File
@@ -0,0 +1 @@
代理商后台入口https://dl.vpiaotong.com/#/login 邀请码:K1N9TP 密码:www.bbitcn.com
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
@@ -0,0 +1,206 @@
票通电子发票平台对接指引
V1.0
北京票通信息技术有限公司
2024 年 10 月 16 日
修订文档历史记录
日期
版本
2024-10-16
<1.0>
说明
编写初稿,包括对接文档及 sdk 获取,企业注册开通说明,
开票场景,冲红场景,数电账号管理、补充场景说明等内容
作者
饶森林
本文档基于票通电子发票平台提供的接口能力进行说明,为合作伙伴对接票通系统时提
供简要指引和参考。
一、对接前事项
票通提供对接服务,随时可启动对接。对接前合作伙伴或客户需提供一些简单的信息。
接入方名称:
接入方联系人及电话:
接入方产品类型:企业定制开发产品、企业私有化部署系统、SaaS 产品
接入方产品所属行业:餐饮收银、物业收银、停车收费、供热收费等等
接入方需要的开票场景:扫码开票、订单开票、小程序开票、支付开票等
接入方预计服务的企业数量:用于评估需要接入的接口范围
该信息可发给票通商务人员,或直接发到对接沟通群。
二、如何启动对接
票通商务人员确认需要提供对接服务时,建立微信群,拉入相关的人员,票通侧需拉入
产品人员、对接人员、服务或运营人员。客户侧视客户情况拉入相关人员,一般建议有商务、
产品和研发人员。
在微信群,票通对接人员发送标准接口文档《票通数电发票接口文档 3.X.X.pdf》和 SDK
工具包,目前 SDK 工具包支持 Java 和 C#两种开发语言。
对接所需的测试环境及参数信息:如平台编码,RSA 签名验签的证书,3des 加密秘钥,
可用于测试开票的税号等由票通对接人员提供。接入方系统开发完成,正式上线前,联系票
通对接人员提供正式环境的参数信息。
正式对接前,可先参考该文档,如有疑问可在微信群组织会议沟通。
三、企业注册开通
要通过接口开具发票,需要开票的企业先在票通平台完成入驻流程,完成入驻有以下几
种方法。(联调测试阶段,可使用票通对接人员提供测试税号)
1)客户在票通平台直接注册
客户可以在票通企业版平台申请注册(注册地址:https://fpkj.vpiaotong.com/register
也可以在票通集团版(票通集团版账号可联系票通商务人员获取)的机构管理功能中添加需
要入驻的企业。申请提交后,需要票通平台运营人员或有权限的代理商进行审核。该过程如
有问题可联系商务人员沟通。
2)接口注册
针对企业数量较多的平台,可通过票通接口完成注册,使用上述接口文档中的“2.2.注
册企业”提交注册信息。接口注册的企业,无登录密码,用户如需使用票通平台,可在票通
平台,通过注册时预留的手机号,获取短信验证码完成密码设置,使用设置后的密码即可登
录票通平台。
基础文档仅提供了提交注册接口,如需获取票通的审核状态,可通过查询接口或推送接
口获取审核状态,如需接口可联系票通对接人员提供。
3)可由票通代理商协助完成注册
联系票通代理商进行操作。
通过接口注册的企业,平台和企业的绑定关系自动生成,如果是使用票通产品功能完成
的注册或已经在票通完成入驻的企业,需要联系票通商务人员或运营人员完成接入方平台和
开票企业的绑定关系。
四、主要的开票场景
一般情况下,仅完成基础开票,对接少量的一至两个接口即可。根据开票场景不同,我
们分别介绍。
1)扫码开票
扫码开票是票通提供的特色能力,针对一些当面交易场景,企业的顾客消费完成后,现
场获取交易小票,可在小票后追加打印开票二维码,顾客可自行扫码,填写完成开票。
顾客侧操作如下图示意:
该过程中,二维码信息生成,可调用票通接口文档中提供的“2.16.获取开票二维码”
接入方系统传入交易相关信息,如商品、单价、数量、税率等,票通为该笔交易生成对应的
开票链接和二维码并同步返回给接入方,接入方系统展示或打印二维码到小票上。
用户扫码后,看到的开票界面是由票通提供,该界面集成了发票抬头模糊检索,获取微
信或支付宝抬头,默认记录上次开票的发票抬头等功能,可方便顾客快速填充抬头信息,并
支持填写邮箱地址和手机号,票通会自动发送邮件,已开通短信服务的企业票通也会自动发
送短信推送发票。票通开票 H5 支持扫描多个二维码合并开票,或将一个二维码拆分开票,
商户自行设置即可。
提交发票后,还可授权将发票插入微信发票卡包或支付宝发票管家,发票开具成功后,
票通自动推到发票到顾客的微信发票卡包或支付宝发票管家。
平台发票开具成功,会调用“2.13.推送发票主要信息”接口推送开具成功的发票,接入
方也可调用“2.18.查询二维码开票信息”主动查询二维码开票状态。
如果用户侧产生退货,未开票情况下可调用“2.17.批量作废开票二维码”直接作废开票
二维码。如果二维码已开票,可调用“2.10.快捷冲红数电发票(全额冲红)”冲红已开具发
票。
注意:2.10、2.13、2.17、2.18 为可选接口,如不对接接口,相关操作也可以通过票通
产品功能完成。
2)直接开票
直接开票接口是通用的开票能力,系统接入方组织好待开票信息后,直接调用“2.9.开
具蓝字数电发票”接口完成开票申请提交,票通服务会实时返回结果,并附带一个链接,通
过该链接可打开 H5 界面实时查看发票开具状态。
票通接收开票申请后,处理开票过程,开票成功或开票异常,都会通过“2.13.推送发票
主要信息”接口,推送相关的信息。
目前数电票开具,需要开票人保持电子税务局账号的登录状态,如果需要开票人员进行
登录认证或风险认证时,票通将会 2.13 接口返回 3999 的特定错误状态,该状态表明需要提
供开票人完成相关认证。数电账号的认证问题,会在后续第六章节详细说明。
注意:提交开票时,发票请求流水号字段,唯一代表一张发票,同一个发票请求流水号
不会重复开票,接入方系统遇到一些场景需重试开票时,如果为同一张发票,请不要变更发
票请求流水号,否则可能有导致重开发票的风险。
3)单据开票
票通集团版提供的单据管理的能力,支持拆分开票、合并开票、支持直接传入订单信息
后续补充发票抬头等进行开票。该接口并非基础能力接口,如有需要财务人员介入进行审核
或拆分合并开票的场景,可使用该套接口能力,联系票通商务人员,安排提供相关的接口文
档,该功能的使用需依赖票通集团版系统。
五、冲红场景
当顾客产生退货或发票开具错误时,票通提供了多种冲红操作能力,票通企业版、集团
版均提供有手工冲红和一键冲红的能力,接口能力也提供了快捷冲红(1 个接口)和全场景
冲红(需 4-8 个接口组合使用)
数电票冲红需要发起红字确认单,确认单申请通过后才可发起真正的冲红操作,流程相
对较长,接口较多,一般建议接入方系统使用快捷冲红接口即可,快捷冲红接口服务对冲红
逻辑进行了封装,由票通整合红字信单申请管理和冲红功能,有效减轻接入方系统的开发工
作量。
1)快捷冲红
快捷冲红时,如仅需全部冲红,可对接“2.10.快捷冲红数电发票(全额冲红)”,该接口
参数简单;如需支持部分冲红,可对接“2.37.快捷冲红数电发票(全额冲红、部分冲红)”,
该接口参数比较完整,支持场景更多,部分冲红和全额冲红都支持,如果需要进行部分冲红,
建议调用“2.36.初始化红字信息确认单”完成初始化,该初始化的目的是加载剩余可冲红的
发票信息,无需接入方系统自行管理剩余可冲红的商品信息。
2)全场景冲红管理
全场景冲红需对接“2.28.红字发票确认单申请”、“2.29.查看红字发票确认单”、“2.30.
开具红字数电发票”、“2.31.红字发票确认单审核”、“2.32.红字发票确认单撤销”、“2.33.红
字发票确认单查询(下载)”、“2.34.获取红字发票确认单查询(下载)结果”、
“2.36.初始化
红字信息确认单”接口,用于精细化管理红字发票确认单及冲红。
销方申请冲红管理流程如下:
发票冲红流程及事项:
数电发票冲红,均需发起红字发票确认单申请,
普通发票申请,如果对方未进行入账操作,则申请后无需确认,调用红字信息表查询接
口获取到已确认(或无需确认)状态后,可进行冲红。
专用发票申请,如果对方未入账未勾选,则申请后无需确认,其他情况需对方确认,对
方确认通过后,调用红字信息表查询接口获取已确认(或无需确认)状态,接下来可进行冲
红。
对方企业发起的红字信息表,可通过红字信息表查询(下载)接口获取红字信息表,对
需要审核的红字信息表,可通过红字信息表审核接口,进行拒绝或者通过。
数电发票(普通发票)可冲红对应的增值税普通发票,包括普通电子发票。
数电发票(增值税专用发票),可冲红对应的增值税专用发票,包括专用电子发票。
接口同时支持购方申请红字确认单,冲红动作需销方执行。
注:冲红接口为非必须对接的接口,有些开发资源紧张或财务人员可人工处理冲红的情
况下,可以不对接冲红接口。或者将冲红功能放入后续的迭代开发。
六、数电账号管理
数电发票的开具,需要用户先在票通平台完成电子税务局账号的登录和风险认证。对于
接入方系统期望在系统内完成认证过程的,可使用票通提供的接口能力。如接入方系统仅需
完成开票,不关注认证过程,可使用票通成熟的认证方式。可通过票通企业版、集团版、微
票通 APP、票通云小程序、票通云服务公众号都可完成认证。推荐使用票通云公众号,该方
式认证无需登录票通账号,可直接在公众号进行数电账号的绑定、认证和对未认证导致开
票失败的发票进行重开。
1)接口能力
票通提供了“2.3.数电账号登记”、“2.4.获取登录短信验证码”、“2.5.短信登录”、“2.6.
获取实名认证二维码”、
“2.7.查询实名认证二维码扫码状态”
“2.8.查询数电账号认证状态”、
“2.45.查询数电账号列表”
、“2.46.退出电子税局登录”等接口完成数电账号的认证。
这里支持几种场景。
1. 接入方系统仅需要数电账号信息,用于开票时指定开票人,可使用 2.45 接口获取
在票通维护好的数电账号及对应状态即可。在开票时传入开票人信息。
2. 接入方系统需进行是数电账号维护的,可调用 2.3 数电账号登记接口,该接口为实
时接口,可验证用户输入的账号密码是否正确。
3. 接入方系统如需完成认证,需首先通过 2.3 或 2.45 获取登录信息,然后调用 2.4 和
2.5 完成短信登录认证,使用 2.6、2.7 完成风险码扫码认证。如需查询各状态的登录或认证
状态,可通过 2.8 接口完成认证。
2)票通云公众号
票通云服务公众号提供了您和您的数电账号关联的两种方式,一种是通过在企业版或集
团版对应数电账号后的关注二维码,扫码关注后,建立用户和数电账号的绑定关系。一种是
直接在票通云公众号-数电认证功能,通过输入手机号或电子税务局的登录账号密码完成验
证,获取属于用户的数电账号列表进行绑定。
“票通云服务”公众号提供的能力:
1. 用户绑定的账号未登录或未认证,导致发票开票失败时,票通将会通过消息通知进
行提醒,用户可直接点击通知,进入到认证界面,完成相应的认证,认证完成后可直接选择
是否重开发票及重开发票的范围(当前失败的发票或 30 天内因为该错误导致失败的发票)
2. 可通过票通云服务-数电认证功能,主动完成登录认证或风险认证,该场景适用于
一些企业为提高顾客体验,要求企业员工按时认证的情况,数电认证功能,无论当前账号处
于何种状态,都可以随时进行再次认证。
3.支持退出登录,目前通过短信方式完成登录,可保持很长的登录状态。
注意:由于目前短信登录方式保持登录的有效期很长,在 PC 端登录进行审核或其他操
作时,可能会被票通平台挤掉,可提醒用户先在票通平台退出登录,然后在电子税务局 PC
端进行操作。退出登录操作可使用票通产品或公众号,也可以通过接口 2.46 完成。
七、补充场景
1)发票抬头获取接口
接入方系统如需自行开发 H5 或需要帮助用户补充抬头信息时,可使用发票抬头获取接
口,该接口非基础接口,请联系商务人员获取。
2)开票项目智能赋码接口
接入方系统如需管理大量的商品品目时,可通过智能赋码接口完成对商品的税收分类编
码设置。该接口非基础接口,请联系商务人员获取。
3SaaS 平台类对接工单接口
接入方系统如果服务的企业较多,需要完善的线上原因流程支持,可联系票通商务人员
和对接人员,提供工单的接口支持,工单接口包括套餐(订单)创建、企业绑定、套餐续费
等能力。
八、更多支持
票通平台提供了多种场景组合的接口能力,如需交流沟通,可在微信群直接沟通或组织
会议进行沟通。