[工具]获取keystore签名,APK签名和包名

        在接大厂MSDK的时候,因为签名的问题,影响了接SDK登录的进程,为了避免这个错误和重复操作:打开cmd输入命令来查看keystore的签名和apk和签名以及包名,因此写了个简单工具。之前有学过Windows API,但也只是用来学习DirectX,简单了解一下创建窗口流程和消息机制。界面那是什么?不了解。不想用MFC,主要是没装,网上搜索了解了下,实现了2个界面:对话框模式和纯代码模式。
        1. 对话框模式


        2. 纯代码模式


        果然纯代码模式符合程序美。
        代码其实很简单,就懒的注释了,核心就是利用cmd命令来获取结果,然后截取想要的字符串就OK了。用到了2个exe文件,一个是在android-sdk\build-tools\28.0.1\中的aapt.exe,另一个则是JDK\bin\中的keytool.exe。有一个坑是配置好环境变量后,需要关闭IDE,cmd命令才执行成功。

代码:

#define WIN32_LEAN_AND_MEAN

#define APP_NAME TEXT("GetCertificate")
#define APP_WIDTH 600
#define APP_HEIGHT 480

#define IDBUTTON_KEYSTORE 1001
#define IDBUTTON_APK 1002
#define IDBUTTON_COMMAND 1003

#define IDEDIT_KEYSTORE 2001
#define IDEDIT_KEYSTOREPASSWORD 2002
#define IDEDIT_APK 2003
#define IDEDIT_RESULT 2004

#include <Windows.h>
#include "resource.h"
#include <commdlg.h>
#include <iostream>
#include <fstream>

#pragma region Global Variables
HINSTANCE g_hInstance;
bool bUseResource = true;//是否使用对话框

HWND hEditKeystorePath;
HWND hEditKeystorePassword;
HWND hEditApkPath;
HWND hEditResult;

HWND hDialog;
#pragma endregion

#pragma region Declaration: Create UI
bool CreateUI(HWND hwnd);
bool CreateCustomDialog(HWND hwnd);
bool CreateCustomCodeView(HWND hwnd);
#pragma endregion

#pragma region Declaration: UI Event
void OnBtnKeystoreClick();
void OnBtnApkClick();
void OnBtnCommandClick();
#pragma endregion

#pragma region Declaration: Get Need Variables
bool GetKeystprepath(char* path, int length);
bool GetPassword(char* password, int length);
bool GetApkPath(char* path, int length);
#pragma endregion

#pragma region Declaration: Get Result
void GetKeystoreResult(const char* path, char* password, char* result, int length);
void GetApkResult(const char* path, char* result, int length);
void GetApkPackageName(const char* path, char* result, int length);
#pragma endregion

#pragma region Declaration: Message Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
#pragma endregion

#pragma region Main Entry
int WINAPI WinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nCmdShow)
{
	g_hInstance = hInstance;

	WNDCLASSEX wc;
	wc.cbClsExtra = 0;
	wc.cbSize = sizeof(wc);
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hIconSm = wc.hIcon;
	wc.hInstance = hInstance;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = APP_NAME;
	wc.lpszMenuName = nullptr;
	wc.style = CS_HREDRAW | CS_VREDRAW;

	if (!RegisterClassEx(&wc))
	{
		MessageBox(NULL, TEXT("RegisterClassEx function - failed"), TEXT("Error"), MB_OK | MB_ICONERROR);
		return 0;
	}

	int screenWidth = GetSystemMetrics(SM_CXSCREEN);
	int screenHeight = GetSystemMetrics(SM_CYSCREEN);
	int posX = (screenWidth - APP_WIDTH) / 2;
	int posY = (screenHeight - APP_HEIGHT) / 2;

	HWND hwnd = CreateWindowEx(WS_EX_APPWINDOW,
		wc.lpszClassName,
		wc.lpszClassName,
		WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
		posX, posY,
		APP_WIDTH, APP_HEIGHT,
		NULL,
		NULL,
		hInstance,
		nullptr);
	if (!hwnd)
	{
		MessageBox(NULL, TEXT("CreateWindowEx function - failed"), TEXT("Error"), MB_OK | MB_ICONERROR);
		return 0;
	}

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	MSG msg;
	memset(&msg, 0, sizeof(msg));

	while (msg.message != WM_QUIT)
	{
		if (GetMessage(&msg, NULL, 0, 0))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int)msg.wParam;
}
#pragma endregion

#pragma region Implementation: Create UI
bool CreateUI(HWND hwnd)
{
	bool result = false;

	if (bUseResource)
		result = CreateCustomDialog(hwnd);
	else
		result = CreateCustomCodeView(hwnd);

	return result;
}

bool CreateCustomDialog(HWND hwnd)
{
	hDialog = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_MAIN), hwnd, (DLGPROC)DlgProc);
	if (!hDialog)
	{
		MessageBox(NULL, TEXT("CreateDialog function - failed"), TEXT("Error"), MB_OK | MB_ICONERROR);
		return false;
	}
	ShowWindow(hDialog, SW_SHOW);
	
	return true;
}

bool CreateCustomCodeView(HWND hwnd)
{
	HWND hStaticKeystore = CreateWindow(
		TEXT("Static"),
		TEXT("Keystore证书:"),
		WS_VISIBLE | WS_CHILD | SS_RIGHT | SS_SIMPLE,
		20, 20,
		120, 20,
		hwnd,
		NULL,
		g_hInstance,
		nullptr);
	if (!hStaticKeystore)
		return false;

	hEditKeystorePath = CreateWindow(
		TEXT("Edit"),
		nullptr,
		WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
		140, 20,
		310, 20,
		hwnd,
		(HMENU)IDEDIT_KEYSTORE,
		g_hInstance,
		nullptr);
	if (!hEditKeystorePath)
		return false;

	HWND hBtnKeystore = CreateWindow(
		TEXT("Button"),
		TEXT("打开文件"),
		WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
		470, 20,
		70, 20,
		hwnd,
		(HMENU)IDBUTTON_KEYSTORE,
		g_hInstance,
		nullptr);
	if (!hBtnKeystore)
		return false;

	HWND hStaticKeystorePassword = CreateWindow(
		TEXT("Static"),
		TEXT("Keystore密码:"),
		WS_VISIBLE | WS_CHILD | SS_RIGHT | SS_SIMPLE,
		20, 60,
		120, 20,
		hwnd,
		NULL,
		g_hInstance,
		nullptr);
	if (!hStaticKeystorePassword)
		return false;

	hEditKeystorePassword = CreateWindow(
		TEXT("Edit"),
		nullptr,
		WS_VISIBLE | WS_CHILD | WS_BORDER | ES_PASSWORD | ES_AUTOHSCROLL,
		140, 60,
		310, 20,
		hwnd,
		(HMENU)IDEDIT_KEYSTOREPASSWORD,
		g_hInstance,
		nullptr);
	if (!hEditKeystorePassword)
		return false;

	HWND hStaticApk = CreateWindow(
		TEXT("Static"),
		TEXT("APK文件:"),
		WS_VISIBLE | WS_CHILD | SS_RIGHT | SS_SIMPLE,
		20, 100,
		120, 20,
		hwnd,
		NULL,
		g_hInstance,
		nullptr);
	if (!hStaticApk)
		return false;

	hEditApkPath = CreateWindow(
		TEXT("Edit"),
		nullptr,
		WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
		140, 100,
		310, 20,
		hwnd,
		(HMENU)IDEDIT_APK,
		g_hInstance,
		nullptr);
	if (!hEditApkPath)
		return false;

	HWND hBtnApk = CreateWindow(
		TEXT("Button"),
		TEXT("打开文件"),
		WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
		470, 100,
		70, 20,
		hwnd,
		(HMENU)IDBUTTON_APK,
		g_hInstance,
		nullptr);
	if (!hBtnApk)
		return false;

	HWND hBtnCommand = CreateWindow(
		TEXT("Button"),
		TEXT("执行"),
		WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
		300 - 35, 150,
		70, 20,
		hwnd,
		(HMENU)IDBUTTON_COMMAND,
		g_hInstance,
		nullptr);
	if (!hBtnCommand)
		return false;

	HWND hStaticResult = CreateWindow(
		TEXT("Static"),
		TEXT("结果:"),
		WS_VISIBLE | WS_CHILD | SS_RIGHT | SS_SIMPLE,
		10, 170,
		120, 20,
		hwnd,
		NULL,
		g_hInstance,
		nullptr);
	if (!hStaticResult)
		return false;

	hEditResult = CreateWindow(
		TEXT("Edit"),
		nullptr,
		WS_VISIBLE | WS_CHILD | WS_BORDER | ES_READONLY | ES_AUTOVSCROLL | ES_MULTILINE,
		10, 200,
		APP_WIDTH - 35, APP_HEIGHT - 40 - 200,
		hwnd,
		(HMENU)IDEDIT_RESULT,
		g_hInstance,
		nullptr);
	if (!hEditResult)
		return false;

	return true;
}
#pragma endregion

#pragma region Implementation: UI Event
void OnBtnKeystoreClick()
{
	char keystorePath[MAX_PATH] = { 0 };

	OPENFILENAME ofn;
	memset(&ofn, 0, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.lpstrFilter = TEXT("keystore Files (*.keystore)\0*.keystore\0All Files (*.*)\0*.*\0");
	ofn.lpstrFile = keystorePath;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = TEXT("keystore");    //默认的打开的文件类型
	ofn.lpstrInitialDir = TEXT(".\\");     //默认的打开的文件路径,这里以当前目录为例

	if (GetOpenFileName(&ofn))
	{
		if (bUseResource)
		{
			if (hDialog != NULL)
			{
				HWND hTempEditKeystore = GetDlgItem(hDialog, IDC_KEYSTORE_FILE_PATH);
				SetWindowText(hTempEditKeystore, keystorePath);
			}
		}
		else
			SetWindowText(hEditKeystorePath, keystorePath);
	}
}

void OnBtnApkClick()
{
	char apkPath[MAX_PATH] = { 0 };
	OPENFILENAME ofn;
	memset(&ofn, 0, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.lpstrFilter = TEXT("APK Files (*.apk)\0*.apk\0All Files (*.*)\0*.*\0");
	ofn.lpstrFile = apkPath;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = TEXT("apk");
	ofn.lpstrInitialDir = TEXT(".\\");
	
	if (GetOpenFileName(&ofn))
	{
		if (bUseResource)
		{
			if (hDialog != NULL)
			{
				HWND hTempEditApk = GetDlgItem(hDialog, IDC_APK_FILE_PATH);
				SetWindowText(hTempEditApk, apkPath);
			}
		}
		else
			SetWindowText(hEditApkPath, apkPath);
	}
}

void OnBtnCommandClick()
{
	char keystorePath[MAX_PATH] = { 0 };
	char keystorePassword[MAX_PATH] = { 0 };
	char apkPath[MAX_PATH] = { 0 };
	bool bIsKeystorepathCorrect = GetKeystprepath(keystorePath, MAX_PATH);
	bool bIsPasswordCorrect = false;
	if (bIsKeystorepathCorrect)
	{
		bIsPasswordCorrect = GetPassword(keystorePassword, MAX_PATH);
	}
	bool bIsApkpathCorrect = GetApkPath(apkPath, MAX_PATH);

	char keystoreMD5[MAX_PATH] = { 0 };
	char apkMD5[MAX_PATH] = { 0 };
	char apkPackageName[MAX_PATH] = { 0 };

	char result[MAX_PATH] = { 0 };
	if (bIsKeystorepathCorrect && bIsPasswordCorrect)
	{
		GetKeystoreResult(keystorePath, keystorePassword, keystoreMD5, MAX_PATH);
		if (keystoreMD5[0] != '\0')
		{
			char descStr[] = "Ketstore MD5: [";
			strcpy_s(result, strlen(descStr) + 1, descStr);
			strcat_s(result, strlen(result) + strlen(keystoreMD5) + 1, keystoreMD5);
			strcat_s(result, strlen(result) + strlen("]") + 1, "]");
		}
	}
	if (bIsApkpathCorrect)
	{
		GetApkResult(apkPath, apkMD5, MAX_PATH);
		GetApkPackageName(apkPath, apkPackageName, MAX_PATH);
		if (apkMD5[0] != '\0')
		{
			if (keystoreMD5[0] != '\0')
			{
				char descStr[] = "\r\nAPK MD5: [";//\r\n 换行
				strcat_s(result, strlen(result) + strlen(descStr) + 1, descStr);
			}
			else
			{
				char descStr[] = "APK MD5: [";
				strcpy_s(result, strlen(descStr) + 1, descStr);
			}
			strcat_s(result, strlen(result) + strlen(apkMD5) + 1, apkMD5);
			strcat_s(result, strlen(result) + strlen("]") + 1, "]");
			char descStr[] = "\r\nAPK PackageName: [";
			strcat_s(result, strlen(result) + strlen(descStr) + 1, descStr);
			strcat_s(result, strlen(result) + strlen(apkPackageName) + 1, apkPackageName);
			strcat_s(result, strlen(result) + strlen("]") + 1, "]");
			if (keystoreMD5[0] != '\0')
			{
				if (strcmp(keystoreMD5, apkMD5) != 0)
				{
					char descStr[] = "\r\nKetstore MD5 和APK MD5不一致";
					strcat_s(result, strlen(result) + strlen(descStr) + 1, descStr);
				}
				else
				{
					char descStr[] = "\r\nKetstore MD5 和APK MD5一致";
					strcat_s(result, strlen(result) + strlen(descStr) + 1, descStr);
				}
			}
		}
	}
	
	if (bUseResource)
	{
		if (hDialog != NULL)
		{
			HWND hTempEditResult = GetDlgItem(hDialog, IDC_RESULT);
			SetWindowText(hTempEditResult, result);
		}
	}
	else
		SetWindowText(hEditResult, result);
}
#pragma endregion

#pragma region Implementation: Get Need Variables
bool GetKeystprepath(char* path, int length)
{
	if (bUseResource)
	{
		if (hDialog != NULL)
		{
			HWND hTempEditKeystorePath = GetDlgItem(hDialog, IDC_KEYSTORE_FILE_PATH);
			GetWindowText(hTempEditKeystorePath, path, MAX_PATH);
		}
	}else
		GetWindowText(hEditKeystorePath, path, MAX_PATH);

	if ('\0' == path[0])
		return false;

	char extensionName[MAX_PATH] = { 0 };
	_splitpath_s(path, nullptr, 0, nullptr, 0, nullptr, 0, extensionName, MAX_PATH);
	if (strcmp(extensionName, ".keystore") != 0)
	{
		TCHAR errorMessage[MAX_PATH] = { 0 };
		wsprintf(errorMessage, TEXT("文件不是keystore文件: %s"), path);
		MessageBox(NULL, errorMessage, TEXT("Error"), MB_OK | MB_ICONERROR);
		return false;
	}
	
	std::ifstream tempFile(path, std::ios::binary);
	if (!tempFile)
	{
		TCHAR errorMessage[MAX_PATH] = { 0 };
		wsprintf(errorMessage, TEXT("文件不存在: %s"), path);
		MessageBox(NULL, errorMessage, TEXT("Error"), MB_OK | MB_ICONERROR);
		return false;
	}

	tempFile.close();

	return true;
}

bool GetPassword(char* password, int length)
{
	if (bUseResource)
	{
		if (hDialog != NULL)
		{
			HWND hTempEditKeystorePassword = GetDlgItem(hDialog, IDC_KEYSTORE_PASSWORD_INPUT);
			GetWindowText(hTempEditKeystorePassword, password, MAX_PATH);
		}
	}
	else
		GetWindowText(hEditKeystorePassword, password, MAX_PATH);

	if ('\0' == password[0])
	{
		MessageBox(NULL, TEXT("请输入密码!"), TEXT("Error"), MB_OK);
		return false;
	}

	return true;
}

bool GetApkPath(char* path, int length)
{
	if (bUseResource)
	{
		if (hDialog != NULL)
		{
			HWND hTempEditApkPath = GetDlgItem(hDialog, IDC_APK_FILE_PATH);
			GetWindowText(hTempEditApkPath, path, MAX_PATH);
		}
	}
	else
		GetWindowText(hEditApkPath, path, MAX_PATH);

	if ('\0' == path[0])
		return false;

	char extensionName[MAX_PATH] = { 0 };
	_splitpath_s(path, nullptr, 0, nullptr, 0, nullptr, 0, extensionName, MAX_PATH);
	if (strcmp(extensionName, ".apk") != 0)
	{
		TCHAR errorMessage[MAX_PATH] = { 0 };
		wsprintf(errorMessage, TEXT("文件不是apk文件: %s"), path);
		MessageBox(NULL, errorMessage, TEXT("Error"), MB_OK | MB_ICONERROR);
		return false;
	}

	std::ifstream tempFile(path, std::ios::binary);
	if (!tempFile)
	{
		TCHAR errorMessage[MAX_PATH] = { 0 };
		wsprintf(errorMessage, TEXT("文件不存在: %s"), path);
		MessageBox(NULL, errorMessage, TEXT("Error"), MB_OK | MB_ICONERROR);
		return false;
	}

	tempFile.close();

	return true;
}
#pragma endregion

#pragma region Declaration: Get Result
void GetKeystoreResult(const char* path, char* password, char* result, int length)
{
	char commandStr[MAX_PATH] = { 0 };
	char autoCommand[] = "echo ";
	char keystoreCommand[] = "keytool -list -v -keystore \"";
	// Auto input password
	strcpy_s(commandStr, strlen(autoCommand) + 1, autoCommand);
	strcat_s(commandStr, strlen(commandStr) + strlen(password) + 1, password);
	strcat_s(commandStr, strlen(commandStr) + strlen("|") + 1, "|");
	// Get the MD5 of the keystore
	strcat_s(commandStr, strlen(commandStr) + strlen(keystoreCommand) + 1, keystoreCommand);
	strcat_s(commandStr, strlen(commandStr) + strlen(path) + 1, path);
	strcat_s(commandStr, strlen(commandStr) + strlen("\"") + 1, "\"");

	FILE* file = _popen(commandStr, "r");
	if (file != nullptr)
	{
		char temp[MAX_PATH] = { 0 };
		char* tempResult = fgets(temp, MAX_PATH, file);
		char findStr[] = "MD5:  ";
		while (tempResult != nullptr)
		{
			char* md5 = strstr(temp, findStr);
			if (md5 != nullptr)
			{
				strncpy_s(result, length, md5 + strlen(findStr), strlen(md5) - strlen(findStr) - 1);
				break;
			}
			else
				tempResult = fgets(temp, MAX_PATH, file);
		}
		_pclose(file);
	}
}

void GetApkResult(const char* path, char* result, int length)
{
	char commandStr[MAX_PATH] = { 0 };
	char command[] = "keytool -printcert -jarfile \"";
	strcpy_s(commandStr, strlen(command) + 1, command);
	strcat_s(commandStr, strlen(commandStr) + strlen(path) + 1, path);
	strcat_s(commandStr, strlen(commandStr) + strlen("\"") + 1, "\"");

	FILE* file = _popen(commandStr, "r");
	if (file != nullptr)
	{
		char temp[MAX_PATH] = { 0 };
		char* tempResult = fgets(temp, MAX_PATH, file);
		char findStr[] = "MD5:  ";
		while (tempResult != nullptr)
		{
			char* md5 = strstr(temp, findStr);
			if (md5 != nullptr)
			{
				strncpy_s(result, length, md5 + strlen(findStr), strlen(md5) - strlen(findStr) - 1);
				break;
			}else
				tempResult = fgets(temp, MAX_PATH, file);
		}
		_pclose(file);
	}
}

void GetApkPackageName(const char* path, char* result, int length)
{
	char commandStr[MAX_PATH] = { 0 };
	char command[] = "aapt dump badging \"";
	strcpy_s(commandStr, strlen(command) + 1, command);
	strcat_s(commandStr, strlen(commandStr) + strlen(path) + 1, path);
	strcat_s(commandStr, strlen(commandStr) + strlen("\"") + 1, "\"");

	FILE* file = _popen(commandStr, "r");
	if (file != nullptr)
	{
		char temp[MAX_PATH] = { 0 };
		char* tempResult = fgets(temp, MAX_PATH, file);
		char findStr[] = "package: name=";
		while (tempResult != nullptr)
		{
			char* packageName = strstr(temp, findStr);//package: name='com.zp.test' versionCode='1' versionName='1.0'
			if (packageName != nullptr)
			{
				char* nextStr = strstr(packageName + strlen(findStr) + 1, "'");
				strncpy_s(result, length, packageName + strlen(findStr) + 1, strlen(packageName) - strlen(nextStr) - strlen(findStr) - 1);
				break;
			}
			else
				tempResult = fgets(temp, MAX_PATH, file);
		}
		_pclose(file);
	}
}
#pragma endregion

#pragma region Implementation: Message Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_CREATE:
		{
			bool result = CreateUI(hwnd);
			if (!result)
				PostQuitMessage(0);
		}
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_KEYDOWN:
		{
			switch ((char)wParam)
			{
			case VK_ESCAPE:
				PostQuitMessage(0);
				break;
			}
		}
		break;
	case WM_COMMAND:
		{
			switch (wParam)
			{
			case IDBUTTON_KEYSTORE:
				OnBtnKeystoreClick();
				break;
			case IDBUTTON_APK:
				OnBtnApkClick();
				break;
			case IDBUTTON_COMMAND:
				OnBtnCommandClick();
				break;
			}
		}
		break;
	}

	return DefWindowProc(hwnd, msg, wParam, lParam);
}

INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	    case WM_COMMAND:
		{
			switch (wParam)
			{
			case IDC_BUTTON_KEYSTORE_FILE_PATH:
				OnBtnKeystoreClick();
				break;
			case IDC_BUTTON_APK_FILE_PATH:
				OnBtnApkClick();
				break;
			case IDC_BUTTON_COMMAND:
				OnBtnCommandClick();
				break;
			}
		}
		break;
	}

	return 0;
}
#pragma endregion

参考文章:

keystore签名

APK签名

Window界面

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值