位置: 编程技术 - 正文

Unity3d接入googleplay内购详细说明(二)(unity快速接入第三方sdk)

编辑:rootadmin

推荐整理分享Unity3d接入googleplay内购详细说明(二)(unity快速接入第三方sdk),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:unity3d接入云渲染,unity chrome,unity3d接入云渲染,unity接入google内购,unity3d接入抖音广告api,unity3d接入云渲染,unity3d接入抖音广告api,unity3d接入 asr,内容如对您有帮助,希望把文章链接给更多的朋友!

因为本文内容比较多,整理花费时间比较长,故分几篇完成,以下为本文目录结构,方便查阅:

Unity3d接入googleplay内购详细说明(一)

引言

一、准备条件:

二、谷歌开发者后台应用创建说明:

Unity3d接入googleplay内购详细说明(二)

三、Unity3d向安卓通信以及接受通信

四、Unity导出安卓Apk正式签名说明

五、使用Eclipse运行unity导出的工程

六、Java代码接入谷歌内购:

七、谷歌内购Java代码

Unity3d接入googleplay内购详细说明(三)

八、Apk上传谷歌商店测试版以及添加测试者

九、Zipalign处理APK文件

十、添加google&#;群组并邀请其成为测试者

十一、测试机googleplay安装以及配置:

Unity3d接入googleplay内购详细说明(四)

十二、真机测试中出现的常见错误以及解决方式:

十三、成功测试购买以及正式版发布

————————————————————————————————————————————————————————————

三、Unity3d向安卓通信以及接受通信

1、作为测试的是临时写的unity3ddemo,只具有最基本的支付功能。首先解决unity安卓通信,这个基本上都是固定的代码。

例子里面分别添加了2种有效商品,后台中sku分别为jb_1,lb_1;

2、其中主要就几句代码,基本通用,需要改的仅是“Pay”方法,以及注意传入的string参数(用来区分不同sku):

private void UnityToAndroid(string buykey)

{

AndroidJavaClass m_unityPlayer = newAndroidJavaClass("com.unity3d.player.UnityPlayer");

AndroidJavaObject m_curActivity = m_unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

m_curActivity.Call("Pay",buykey);

}

3、所有代码如下:

using UnityEngine;

usingSystem.Collections;

public classGUIpay : MonoBehaviour

{

private int i = 0;

private int j = 0;

private string key;//suk

private string MessageFromAndroid;//从安卓端接受的消息

void OnGUI(){

if(GUILayout.Button("金币1",GUILayout.Height(),GUILayout.Width())){

i&#;&#;;

key="jb_1";

UnityToAndroid(key);

}

if(GUILayout.Button("&#;包1",GUILayout.Height(),GUILayout.Width())){

j&#;&#;;

key ="lb_1";

UnityToAndroid(key);

}

GUILayout.Button("购买:"&#;key&#;"分别:"&#;i.ToString()&#;"次/"&#;j.ToString()&#;"次",GUILayout.Height(),GUILayout.Width());

GUILayout.Button("AndroidMessage:"&#;MessageFromAndroid,GUILayout.Height(),GUILayout.Width());

}

//安卓支付通信

private void UnityToAndroid(stringbuykey){

AndroidJavaClass m_unityPlayer =new AndroidJavaClass("com.unity3d.player.UnityPlayer");

AndroidJavaObject m_curActivity =m_unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

if(m_curActivity == null)

{

Debug.LogError("获得不到JAVA对象");

return;

}

m_curActivity.Call("Pay",buykey);

}

//从安卓端接受消息,因为本脚本挂在一直存在的MainCamera对象上,将从其上获取消息,对应java代码要将消息发送对象指定为MainCamera

void Messgae(string message)

{

MessageFromAndroid = message;

}

}

4、运行效果如下,点击会报错,所以只能在真机上测试:

5、接下来,打开设置,勾选导出安卓工程,icon,应用名字,起始页等如果仅做测试可以不必正式。就是说如果不是正式发布的apk,仅需要正式的包名,正式的签名,正式发布记得修改其他icon、起始页等信息:

6、上传到谷歌商店需要正式的签名,不能为debug模式签名,否则上传失败。签名制作方式如下:

四、Unity导出安卓Apk正式签名说明

一、签名的意义

为了保证每个应用程序开发商合法ID,防止部分开放商可能通过使用相同的Package Name来混淆替换已经安装的程序,我们需要对我们发布的APK文件进行唯一签名,保证我们每次发布的版本的一致性(如自动更新不会因为版本不一致而无法安装)。

这里详细解释一下:

安装包apk的名字,在世界范围内,有众多开发者,很可能命名重复,当命名重复的时候就需要有一个唯一的识别符加以区分。这就是安卓中的签名。这个签名不用向谷歌申请,而是自己在本机签名即可。

这个签名唯一,相当重要,务必保管好,是制作后续版本更新的重要依据。如果更换,即使是同一个应用,也不会覆盖安装,而是单独当做另外一个新的应用安装。而且当你提交应用时,如果更换签名,就不会当成之前应用的后续更新版本。

二、.签名的步骤(仅安卓需要签名)

一般来说,安卓的apk安装包都是由eclipse编译而成,而他自带了签名插件。包括正式签名和dubug测试签名。

而我们这里并不用eclipse,而是利用unity自带签名功能。  

1、工程确认无bug后,需要倒出apk文件时:

2、打开导出设置选项:

3、这publishing settings下面默认是debug签名,如果仅仅自己测试,就不需要修改,上传谷歌商店,需要制作签名。

4、选择 create new keystore,下面选择保存位置,

5、点击key

6、填写详细签名信息,务必记住密码,因为在eclipes里面要使用。存储到可靠位置,丢失后无法找回:

7、证书如下,命令行工具能打开,需要安装jdk:

验证签名信息:

命令行输入 jarsigner -verify -verbose -certs XXX.apk(apk 完整路径) 可以看到 比对签名信息(需要安装了jdk)

8、签名栏默认是unsigned,不签名,debug模式

以后再次导出apk时,必须先选中该签名,还需要在编译前输入原始密码

9、如果为debug签名,那么在上传到谷歌应用商店时会遇到这样情况:

、签名正确即可正确上传

、当然也可以利用eclipes来签名,具体请百度。接下来我们要在eclipes里面接入谷歌内购api:

五、使用Eclipse运行unity导出的工程

1、打开eclipes,创建工作空间。

2、新建安卓工程:

3、填写跟unity一样的包名,其他默认即可,下一步·····下一步

5、这是刚创建的空工程

6、接下来将unity导出的工程,里面的文件夹直接拖动到hnn工程下,选择全部覆盖,我们的工程内容将被替换为unity内容:

7、为了试验一下是否能够在真机上运行,可以编译一下工程:

8、找到编译出来的apk安装到真机上测试,能够启动即可,按钮什么的当然现在还没有作用:

六、Java代码接入谷歌内购:

以下是开发者中心对内购的详细解释,英文文档,可以谷歌翻译一下。链接地址:

In-appBilling Overview

PreparingYour In-app Billing Application

1、首先我们需要下载内购sdk。在eclipes里面可以下载到,目前因为禁止链接外网,下载时可能需要vpn,内购demo文件并不大。

这个是他的官方demo,介绍了如何在安卓工程中接入内购,这就是为什么我们选择unity导出安卓工程,而不选择eclipes打jar文件,放到unity中编译apk:

我们需要的就是:IInAppBillingService.aidl这个文件。

2、将该文件拖入到我们的工程src目录下、路径请自己设定好。

3、此外还需要加入必备的工具类。

也就是util文件夹下面的所有代码。同样拖动到我们的工程目录下,拖进来后可能会报引入路径错,注意修改成符合你自己工程的正常路径。例如我的工程导入完毕后结构大约如此。

从上至下依次为内购必须文件、unity自带类、内购工具类。文件目录结构:

4、AndroidMainfest文件添加权限,如果你还做了其他接入功能,例如分享等,权限做合并。

<uses-permission android:name="com.android.vending.BILLING"/>

5、接着我们在java代码中写入被unity调用以及向unity回传消息的代码。

前文说道:

6、向uinty中发送消息,一般用来传送是否购买成功等等。

7、Unity中接受内容继续做处理:

8、内购方面,需要写入base ras公共密钥。就是前文说的再谷歌开发者后台申请的key

9、再写入sku,这个sku就是内购项目的唯一id,可以从unity中传过来,也可以写在java中,我们这里做传入。

、其他添加内购时的各种监听,是否登录,是否符合测试条件,是否绑定银行卡等等,主要借鉴其demo中的代码。

七、谷歌内购Java代码

以下为unity导出的默认类中所有代码:

packagecom.taojinzhe.hnn;

importcom.unity3d.player.*;

importandroid.app.NativeActivity;

importandroid.content.res.Configuration;

importandroid.graphics.PixelFormat;

importandroid.os.Bundle;

importandroid.view.KeyEvent;

import android.view.MotionEvent;

importandroid.view.View;

importandroid.view.Window;

importandroid.view.WindowManager;

importandroid.app.Activity;

importandroid.app.AlertDialog;

importandroid.content.Intent;

importandroid.content.SharedPreferences;

import android.util.Log;

importandroid.widget.ImageView;

importandroid.widget.Toast;

importcom.util.IabHelper;

importcom.util.IabResult;

importcom.util.Inventory;

importcom.util.Purchase;

public classUnityPlayerNativeActivity extends NativeActivity

{

protected UnityPlayer mUnityPlayer; // don't change the name of thisvariable; referenced from native code

//___________________________________________________________

//The helper object

IabHelper mHelper;

// Debug tag, for logging

static final String TAG ="hongneinei";

// Does the user have the premium upgrade?

boolean mIsPremium = false;

// Does the user have an activesubscription to the infinite gas plan?

boolean mSubscribedToInfiniteGas = false;

// SKUs for our products: the premiumupgrade (non-consumable) and gas (consumable)

static String SKU_consume ="";

static String SKU_noconsume ="";

//static final String SKU_GAS="";

//SKU for our subscription (infinite gas)

//static final String SKU_INFINITE_GAS ="infinite_gas";

// (arbitrary) request code for the purchaseflow

static final int RC_REQUEST = ;

//___________________________________________________________

// Setup activity layout

@Override protected void onCreate (BundlesavedInstanceState)

{

requestWindowFeature(Window.FEATURE_NO_TITLE);

super.onCreate(savedInstanceState);

getWindow().takeSurface(null);

setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);

getWindow().setFormat(PixelFormat.RGB_);

mUnityPlayer = newUnityPlayer(this);

if (mUnityPlayer.getSettings().getBoolean ("hide_status_bar", true))

getWindow ().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(mUnityPlayer);

mUnityPlayer.requestFocus();

//___________________________________________________________

/* baseEncodedPublicKey should be YOURAPPLICATION'S PUBLIC KEY

* (that you got from the Google Playdeveloper console). This is not your

* developer public key, it's the*app-specific* public key.

*

* Instead of just storing the entireliteral string here embedded in the

* program, construct the key at runtime from pieces or

* use bit manipulation (for example,XOR with some other string) to hide

* the actual key. The key itself is not secret information, butwe don't

Unity3d接入googleplay内购详细说明(二)(unity快速接入第三方sdk)

* want to make it easy for an attackerto replace the public key with one

* of their own and then fake messagesfrom the server.

*/

String baseEncodedPublicKey ="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtBVdoRrdD/oCWHYgzhT4TBIh0AZn0Sf1uXD8gWQ1H9LdpOB4MX4QG9RP9pBbS0e6W8f7EbjKyicEa6LetTxpg1Gf&#;3N0L0c9E2G3RwIO9SXaRUNfzjHN2lzaspLQKj5&#;SLpT8JD6ISVuro7OS4nxmi7xQT0lx/dPOAOs8mQ/1qmlgwsJRybqWQ&#;hAvu1fchMggTTAyG1RyqKTJNErlNYTvog7ZQjjvCZXW5aDBnGeEjFoILnt5XAoaUpuObmbkoCOkJeSiUTQqD&#;mqAQCvXnBhUso2klLDlOhTz0FUT7XKZOU1Q&#;lXqNlJ6GurXzhFguvhdo&#;3DQIDAQAB";

// Create the helper, passing it our contextand the public key to verify signatures with

Log.d(TAG, "Creating IABhelper.");

mHelper = new IabHelper(this,baseEncodedPublicKey);

// enable debug logging (for aproduction application, you should set this to false).

mHelper.enableDebugLogging(true);

// Start setup. This is asynchronousand the specified listener

// will be called once setup completes.

Log.d(TAG, "Startingsetup.");

mHelper.startSetup(newIabHelper.OnIabSetupFinishedListener() {

public voidonIabSetupFinished(IabResult result) {

Log.d(TAG, "Setupfinished.");

if (!result.isSuccess()) {

// Oh noes, there was a problem.

complain("Problemsetting up in-app billing: " &#; result);

return;

}

// Have we been disposed of inthe meantime? If so, quit.

if (mHelper == null) return;

// IAB is fully set up. Now, let'sget an inventory of stuff we own.

Log.d(TAG, "Setupsuccessful. Querying inventory.");

mHelper.queryInventoryAsync(mGotInventoryListener);

}

});

//___________________________________________________________

}

//___________________________________________________________

protected void Pay(final String buykey)

{

/* TODO: for security, generate your payloadhere for verification. See the comments on

* verifyDeveloperPayload() for more info.Since this is a SAMPLE, we just use

* an empty string, but on a productionapp you should carefully generate this. */

if(buykey.contains("jb_"))

{

SKU_consume = buykey;

}

if(buykey.contains("lb_"))

{

SKU_noconsume = buykey;

}

runOnUiThread(new Runnable()

{

public void run()

{

Toast.makeText(getApplicationContext(),buykey,Toast.LENGTH_SHORT).show();

SendToUnityMessage(buykey);

}

});

String payload = "";

mHelper.launchPurchaseFlow(this,buykey, RC_REQUEST,

mPurchaseFinishedListener,payload);

}

//Listener that's called when we finish querying the items and subscriptions weown 购买侦听器完成

IabHelper.OnIabPurchaseFinishedListenermPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener(){

public void onIabPurchaseFinished(IabResult result, Purchase purchase){

Log.d(TAG, "Purchase finished: " &#; result &#; ", purchase:" &#; purchase);

if (result.isFailure()) {

//complain("Error purchasing: " &#;result);

//setWaitScreen(false);

return;

}

if (!verifyDeveloperPayload(purchase)) {

complain("Error purchasing.Authenticity verification failed.");

// setWaitScreen(false);

return;

}

Log.d(TAG, "Purchase successful.");

if (purchase.getSku().equals(SKU_consume)) {

Log.d(TAG, "Purchase is gas.Starting gas consumption.");

mHelper.consumeAsync(purchase,mConsumeFinishedListener);

}

else if (purchase.getSku().equals(SKU_noconsume)) {

Log.d(TAG, "Purchase ispremium upgrade. Congratulating user.");

alert("Thank you forupgrading to premium!");

mIsPremium = true;

}

}

};

// Listener that's called when we finishquerying the items and subscriptions we own

IabHelper.QueryInventoryFinishedListener mGotInventoryListener = newIabHelper.QueryInventoryFinishedListener() {

public void onQueryInventoryFinished(IabResult result, Inventoryinventory) {

Log.d(TAG, "Query inventoryfinished.");

// Have we been disposed of in themeantime? If so, quit.

if (mHelper == null) return;

// Is it a failure?

if (result.isFailure()) {

complain("Failed to queryinventory: " &#; result);

return;

}

Log.d(TAG, "Query inventorywas successful.");

/*

* Check for items we own. Noticethat for each purchase, we check

* the developer payload to see ifit's correct! See

* verifyDeveloperPayload().

*/

// Do we have the premium upgrade?

Purchase premiumPurchase =inventory.getPurchase(SKU_noconsume);

mIsPremium = (premiumPurchase !=null && verifyDeveloperPayload(premiumPurchase));

Log.d(TAG, "User is " &#;(mIsPremium ? "PREMIUM" : "NOT PREMIUM"));

// Check for gas delivery -- if weown gas, we should fill up the tank immediately

Purchase gasPurchase =inventory.getPurchase(SKU_consume);

if (gasPurchase != null &&verifyDeveloperPayload(gasPurchase)) {

Log.d(TAG, "We have gas.Consuming it.");

mHelper.consumeAsync(inventory.getPurchase(SKU_consume),mConsumeFinishedListener);

return;

}

Log.d(TAG, "Initial inventoryquery finished; enabling main UI.");

}

};

//Called when consumption is complete

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = newIabHelper.OnConsumeFinishedListener() {

public voidonConsumeFinished(Purchase purchase, IabResult result) {

Log.d(TAG, "Consumptionfinished. Purchase: " &#; purchase &#; ", result: " &#; result);

// if we were disposed of in themeantime, quit.

if (mHelper == null) return;

// We know this is the"gas" sku because it's the only one we consume,

// so we don't check which sku wasconsumed. If you have more than one

// sku, you probably shouldcheck...

if (result.isSuccess()) {

// successfully consumed, so weapply the effects of the item in our

// game world's logic, which inour case means filling the gas tank a bit

Log.d(TAG, "Consumptionsuccessful. Provisioning.");

}

else {

complain("Error whileconsuming: " &#; result);

}

Log.d(TAG, "End consumptionflow.");

}

};

/** Verifies the developer payload of apurchase. */

boolean verifyDeveloperPayload(Purchase p){

String payload =p.getDeveloperPayload();

/*

* TODO: verify that the developerpayload of the purchase is correct. It will be

* the same one that you sent wheninitiating the purchase.

*

* WARNING: Locally generating a randomstring when starting a purchase and

* verifying it here might seem like agood approach, but this will fail in the

* case where the user purchases anitem on one device and then uses your app on

* a different device, because on theother device you will not have access to the

* random string you originallygenerated.

*

* So a good developer payload hasthese characteristics:

*

* 1. If two different users purchasean item, the payload is different between them,

* so that one user's purchase can't be replayed to another user.

*

* 2. The payload must be such that youcan verify it even when the app wasn't the

* one who initiated the purchase flow (so that items purchased by the useron

* one device work on other devices owned by the user).

*

* Using your own server to store andverify developer payloads across app

* installations is recommended.

*/

return true;

}

void complain(String message) {

Log.e(TAG, "**** TrivialDrive Error: " &#; message);

alert("Error: " &#; message);

}

void alert(String message) {

AlertDialog.Builder bld = new AlertDialog.Builder(this);

bld.setMessage(message);

bld.setNeutralButton("OK", null);

Log.d(TAG, "Showing alert dialog: " &#; message);

bld.create().show();

}

//___________________________________________________________

//向unity发送消息

void SendToUnityMessage(String Sendmessage)

{

UnityPlayer.UnitySendMessage("MainCamera","Messgae",Sendmessage);

}

// Quit Unity

@Override protected void onDestroy ()

{

mUnityPlayer.quit();

super.onDestroy();

}

// Pause Unity

@Override protected void onPause()

{

super.onPause();

mUnityPlayer.pause();

}

// Resume Unity

@Override protected void onResume()

{

super.onResume();

mUnityPlayer.resume();

}

// This ensures the layout will becorrect.

@Override public voidonConfigurationChanged(Configuration newConfig)

{

super.onConfigurationChanged(newConfig);

mUnityPlayer.configurationChanged(newConfig);

}

// Notify Unity of the focus change.

@Override public voidonWindowFocusChanged(boolean hasFocus)

{

super.onWindowFocusChanged(hasFocus);

mUnityPlayer.windowFocusChanged(hasFocus);

}

// For some reason the multiple keyeventtype is not supported by the ndk.

// Force event injection by overridingdispatchKeyEvent().

@Override public booleandispatchKeyEvent(KeyEvent event)

{

if (event.getAction() ==KeyEvent.ACTION_MULTIPLE)

return mUnityPlayer.injectEvent(event);

returnsuper.dispatchKeyEvent(event);

}

// Pass any events not handled by(unfocused) views straight to UnityPlayer

@Override public boolean onKeyUp(intkeyCode, KeyEvent event) { returnmUnityPlayer.injectEvent(event); }

@Override public boolean onKeyDown(intkeyCode, KeyEvent event) { returnmUnityPlayer.injectEvent(event); }

@Override public booleanonTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }

/*API*/ public boolean onGenericMotionEvent(MotionEventevent) { returnmUnityPlayer.injectEvent(event); }

}

本片结语:

至此,unity,eclipes代码部分基本上完结,接下来我们将编译出apk进行测试。

Unity3d接入googleplay内购详细说明(三) 因为本文内容比较多,整理花费时间比较长,故分几篇完成,以下为本文目录结构,方便查阅:Unity3d接入googleplay内购详细说明(一)引言一、准备条件

Unity3D Vuforia Android 拨打电话 ?xmlversion=1.0encoding=utf-8?manifestxmlns:android=

Unity3D中的Prefab使用方法 Prefabs(预设)是最常用的一种资源类型,是一种可被重复使用的游戏对象。1、特点:1、它可以被置入多个场景中,也可以在一个场景中多次置入。2、当

标签: unity快速接入第三方sdk

本文链接地址:https://www.jiuchutong.com/biancheng/382560.html 转载请保留说明!

上一篇:Unity3d接入googleplay内购详细说明(一)(unity3d接入抖音广告api)

下一篇:Unity3d接入googleplay内购详细说明(三)(unity接入google内购)

  • 公司账户转账给个人怎么合理避税?
  • 会计报税
  • 外籍人员可以在中国工作吗
  • 土地交易服务费属于非税收入吗
  • 企业所得税计算例题
  • 茶叶自产自销成本核算
  • 信息技术费如何做账
  • 劳务分包企业所得税25%
  • 境外企业所得税税率
  • 私人出租房子发票怎么开
  • 周转材料租赁费怎么结转成本
  • 返利回收期怎么计算
  • 税金及附加科目有
  • 外管证报验核销
  • 应交增值税进项税额月底怎么处理
  • 企业减免税会计分录
  • 简易计税方法是什么意思
  • 销售不动产增值税税率变化
  • 服务费计入什么收入
  • 个人所得税合并申报
  • 高新企业最新政策2020
  • 备抵法发生坏账分录
  • 生产成本的计算公式是什么
  • 财政专项补助资金企业所得税申报
  • 事业单位收入需要缴纳所得税吗
  • 路由器2.4g和4g有什么区别
  • 支付中间业务收入怎么算
  • 一般纳税人购进农产品如何抵扣进项税额
  • thinkphp saveall
  • 支付宝流量红包怎么取消
  • 差额征收是啥意思
  • 闲置房的相关政策
  • 销售使用过的机器设备如何缴纳增值税
  • vue3如何实现使用SortableJs插件进行表格内的数据项拖拽排序
  • php中使用js
  • 法定假日的加班费怎么算
  • Chrome谷歌浏览器网页
  • php写文件函数
  • 更改税种需要哪些资料
  • 电子汇票追索是什么意思
  • uni-app实战教程
  • 解决掉发的有效方法
  • 应收账款与主营业务收入的比率
  • 施工水电费账务处理流程
  • linux数据库导入命令
  • mongodb视频教程
  • mongodb reference
  • 速达建账套期初数据
  • 商贸企业的存货周转率什么水平属于正常
  • mysql,if
  • 公司帮员工买的意外险钱是打到公司还是员工账号
  • 交易性金融资产的账务处理
  • 销售边角料税率是多少
  • 印花税不足一元免征吗
  • 留抵税额抵减欠税滞纳金
  • 进项发票可以退税吗
  • 差旅费车票是否可以报销
  • 通信地址需要写什么
  • 购买税控系统分录
  • 企业其他应收款余额非常大的原因
  • 应收账款怎样记账
  • sqlserver中时间类型
  • centos怎么连接远程服务器
  • 苹果电脑怎么打开u盘
  • win7资源管理器停止工作怎么办
  • VirtualBox安装64位系统报错的处理方法
  • linux tar -czvf
  • iptables: Unknown error 4294967295问题解决方法
  • 粒子冲突
  • 全面理解全面把握全面落实
  • 文件上传的三个条件
  • web开发css
  • unity的脚本
  • android新手入门
  • node中的ejs
  • json转复杂对象
  • 教大家使用灭火器
  • 税务总局副局长饶
  • 增值税发票怎么购票
  • 国税和地税是什么
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设