Menggunakan tanda terima untuk memverifikasi pembelian produk
Setiap transaksi Microsoft Store yang menghasilkan pembelian produk yang berhasil dapat secara opsional mengembalikan tanda terima transaksi. Tanda terima ini memberikan informasi tentang produk yang tercantum dan biaya moneter kepada pelanggan.
Memiliki akses ke informasi ini mendukung skenario di mana aplikasi Anda perlu memverifikasi bahwa pengguna membeli aplikasi Anda, atau telah melakukan pembelian add-on (juga disebut produk dalam aplikasi atau IAP) dari Microsoft Store. Misalnya, bayangkan game yang menawarkan konten yang diunduh. Jika pengguna yang membeli konten game ingin memutarnya di perangkat lain, Anda perlu memverifikasi bahwa pengguna sudah memiliki konten. Berikut caranya.
Penting
Artikel ini memperlihatkan cara menggunakan anggota namespace Windows.ApplicationModel.Store untuk mendapatkan dan memvalidasi tanda terima untuk pembelian dalam aplikasi. Jika Anda menggunakan namespace Windows.Services.Store untuk pembelian dalam aplikasi (diperkenalkan di Windows 10, versi 1607, dan tersedia untuk proyek yang menargetkan Windows 10 Anniversary Edition (10.0; Build 14393) atau rilis yang lebih baru di Visual Studio), namespace layanan ini tidak menyediakan API untuk mendapatkan tanda terima pembelian untuk pembelian dalam aplikasi. Namun, Anda dapat menggunakan metode REST di API pengumpulan Microsoft Store untuk mendapatkan data untuk transaksi pembelian. Untuk informasi selengkapnya, lihat Tanda terima untuk pembelian dalam aplikasi.
Meminta tanda terima
Namespace Windows.ApplicationModel.Store mendukung beberapa cara untuk mendapatkan tanda terima:
- Saat Anda melakukan pembelian dengan menggunakan CurrentApp.RequestAppPurchaseAsync atau CurrentApp.RequestProductPurchaseAsync (atau salah satu kelebihan beban lain dari metode ini), nilai pengembalian berisi tanda terima.
- Anda dapat memanggil metode CurrentApp.GetAppReceiptAsync untuk mengambil info tanda terima saat ini untuk aplikasi Anda dan add-on apa pun di aplikasi Anda.
Tanda terima aplikasi terlihat seperti ini.
Catatan
Contoh ini diformat untuk membantu membuat XML dapat dibaca. Tanda terima aplikasi nyata tidak menyertakan spasi kosong di antara elemen.
<Receipt Version="1.0" ReceiptDate="2012-08-30T23:10:05Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="4e362949-acc3-fe3a-e71b-89893eb4f528">
<AppReceipt Id="8ffa256d-eca8-712a-7cf8-cbf5522df24b" AppId="55428GreenlakeApps.CurrentAppSimulatorEventTest_z7q3q7z11crfr" PurchaseDate="2012-06-04T23:07:24Z" LicenseType="Full" />
<ProductReceipt Id="6bbf4366-6fb2-8be8-7947-92fd5f683530" ProductId="Product1" PurchaseDate="2012-08-30T23:08:52Z" ExpirationDate="2012-09-02T23:08:49Z" ProductType="Durable" AppId="55428GreenlakeApps.CurrentAppSimulatorEventTest_z7q3q7z11crfr" />
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>cdiU06eD8X/w1aGCHeaGCG9w/kWZ8I099rw4mmPpvdU=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>SjRIxS/2r2P6ZdgaR9bwUSa6ZItYYFpKLJZrnAa3zkMylbiWjh9oZGGng2p6/gtBHC2dSTZlLbqnysJjl7mQp/A3wKaIkzjyRXv3kxoVaSV0pkqiPt04cIfFTP0JZkE5QD/vYxiWjeyGp1dThEM2RV811sRWvmEs/hHhVxb32e8xCLtpALYx3a9lW51zRJJN0eNdPAvNoiCJlnogAoTToUQLHs72I1dECnSbeNPXiG7klpy5boKKMCZfnVXXkneWvVFtAA1h2sB7ll40LEHO4oYN6VzD+uKd76QOgGmsu9iGVyRvvmMtahvtL1/pxoxsTRedhKq6zrzCfT8qfh3C1w==</SignatureValue>
</Signature>
</Receipt>
Tanda terima produk terlihat seperti ini.
Catatan
Contoh ini diformat untuk membantu membuat XML dapat dibaca. Tanda terima produk nyata tidak termasuk spasi kosong di antara elemen.
<Receipt Version="1.0" ReceiptDate="2012-08-30T23:08:52Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="4e362949-acc3-fe3a-e71b-89893eb4f528">
<ProductReceipt Id="6bbf4366-6fb2-8be8-7947-92fd5f683530" ProductId="Product1" PurchaseDate="2012-08-30T23:08:52Z" ExpirationDate="2012-09-02T23:08:49Z" ProductType="Durable" AppId="55428GreenlakeApps.CurrentAppSimulatorEventTest_z7q3q7z11crfr" />
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
<DigestValue>Uvi8jkTYd3HtpMmAMpOm94fLeqmcQ2KCrV1XmSuY1xI=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>TT5fDET1X9nBk9/yKEJAjVASKjall3gw8u9N5Uizx4/Le9RtJtv+E9XSMjrOXK/TDicidIPLBjTbcZylYZdGPkMvAIc3/1mdLMZYJc+EXG9IsE9L74LmJ0OqGH5WjGK/UexAXxVBWDtBbDI2JLOaBevYsyy+4hLOcTXDSUA4tXwPa2Bi+BRoUTdYE2mFW7ytOJNEs3jTiHrCK6JRvTyU9lGkNDMNx9loIr+mRks+BSf70KxPtE9XCpCvXyWa/Q1JaIyZI7llCH45Dn4SKFn6L/JBw8G8xSTrZ3sBYBKOnUDbSCfc8ucQX97EyivSPURvTyImmjpsXDm2LBaEgAMADg==</SignatureValue>
</Signature>
</Receipt>
Anda dapat menggunakan salah satu contoh tanda terima ini untuk menguji kode validasi Anda. Untuk informasi selengkapnya tentang konten tanda terima, lihat deskripsi elemen dan atribut.
Memvalidasi tanda terima
Untuk memvalidasi keaslian tanda terima, Anda memerlukan sistem back-end Anda (layanan web atau yang serupa) untuk memeriksa tanda tangan tanda terima menggunakan sertifikat publik. Untuk mendapatkan sertifikat ini, gunakan URL https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60
, di mana CertificateId
adalah nilai CertificateId dalam tanda terima.
Berikut adalah contoh proses validasi tersebut. Kode ini berjalan dalam aplikasi konsol .NET Framework yang menyertakan referensi ke rakitan System.Security.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.IO;
using System.Security.Cryptography.Xml;
using System.Net;
namespace ReceiptVerificationSample
{
public sealed class RSAPKCS1SHA256SignatureDescription : SignatureDescription
{
public RSAPKCS1SHA256SignatureDescription()
{
base.KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
base.DigestAlgorithm = typeof(SHA256Managed).FullName;
base.FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
base.DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key);
deformatter.SetHashAlgorithm("SHA256");
return deformatter;
}
public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key);
formatter.SetHashAlgorithm("SHA256");
return formatter;
}
}
class Program
{
// Utility function to read the bytes from an HTTP response
private static int ReadResponseBytes(byte[] responseBuffer, Stream resStream)
{
int count = 0;
int numBytesRead = 0;
int numBytesToRead = responseBuffer.Length;
do
{
count = resStream.Read(responseBuffer, numBytesRead, numBytesToRead);
numBytesRead += count;
numBytesToRead -= count;
} while (count > 0);
return numBytesRead;
}
public static X509Certificate2 RetrieveCertificate(string certificateId)
{
const int MaxCertificateSize = 10000;
// Retrieve the certificate URL.
String certificateUrl = String.Format(
"https://go.microsoft.com/fwlink/?LinkId=246509&cid={0}", certificateId);
// Make an HTTP GET request for the certificate
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(certificateUrl);
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Retrieve the certificate out of the response stream
byte[] responseBuffer = new byte[MaxCertificateSize];
Stream resStream = response.GetResponseStream();
int bytesRead = ReadResponseBytes(responseBuffer, resStream);
if (bytesRead < 1)
{
//TODO: Handle error here
}
return new X509Certificate2(responseBuffer);
}
static bool ValidateXml(XmlDocument receipt, X509Certificate2 certificate)
{
// Create the signed XML object.
SignedXml sxml = new SignedXml(receipt);
// Get the XML Signature node and load it into the signed XML object.
XmlNode dsig = receipt.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0];
if (dsig == null)
{
// If signature is not found return false
System.Console.WriteLine("Signature not found.");
return false;
}
sxml.LoadXml((XmlElement)dsig);
// Check the signature
bool isValid = sxml.CheckSignature(certificate, true);
return isValid;
}
static void Main(string[] args)
{
// .NET does not support SHA256-RSA2048 signature verification by default,
// so register this algorithm for verification.
CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription),
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
// Load the receipt that needs to be verified as an XML document
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("..\\..\\receipt.xml");
// The certificateId attribute is present in the document root, retrieve it
XmlNode node = xmlDoc.DocumentElement;
string certificateId = node.Attributes["CertificateId"].Value;
// Retrieve the certificate from the official site.
// NOTE: For sake of performance, you would want to cache this certificate locally.
// Otherwise, every single call will incur the delay of certificate retrieval.
X509Certificate2 verificationCertificate = RetrieveCertificate(certificateId);
try
{
// Validate the receipt with the certificate retrieved earlier
bool isValid = ValidateXml(xmlDoc, verificationCertificate);
System.Console.WriteLine("Certificate valid: " + isValid);
}
catch (Exception ex)
{
System.Console.WriteLine(ex.ToString());
}
}
}
}
Deskripsi elemen dan atribut untuk tanda terima
Bagian ini menjelaskan elemen dan atribut dalam tanda terima.
Elemen Tanda Terima
Elemen akar file ini adalah elemen Tanda Terima , yang berisi informasi tentang pembelian aplikasi dan dalam aplikasi. Elemen ini berisi elemen anak berikut.
Elemen | Diperlukan | Kuantitas | Deskripsi |
---|---|---|---|
AppReceipt | Tidak | 0 atau 1 | Berisi informasi pembelian untuk aplikasi saat ini. |
ProductReceipt | Tidak | 0 atau lebih | Berisi informasi tentang pembelian dalam aplikasi untuk aplikasi saat ini. |
Tanda Tangan | Ya | 1 | Elemen ini adalah konstruksi XML-DSIG standar. Ini berisi elemen SignatureValue , yang berisi tanda tangan yang dapat Anda gunakan untuk memvalidasi tanda terima, dan elemen SignedInfo . |
Tanda terima memiliki atribut berikut.
Atribut | Deskripsi |
---|---|
Versi | Nomor versi tanda terima. |
CertificateId | Thumbprint sertifikat yang digunakan untuk menandatangani tanda terima. |
Tanda TerimaDate | Tanggal tanda terima ditandatangani dan diunduh. |
ReceiptDeviceId | Mengidentifikasi perangkat yang digunakan untuk meminta tanda terima ini. |
Elemen AppReceipt
Elemen ini berisi informasi pembelian untuk aplikasi saat ini.
AppReceipt memiliki atribut berikut.
Atribut | Deskripsi |
---|---|
Id | Mengidentifikasi pembelian. |
AppId | Nilai Nama Keluarga Paket yang digunakan OS untuk aplikasi. |
Jenis Lisensi | Penuh, jika pengguna membeli versi lengkap aplikasi. Uji coba, jika pengguna mengunduh versi percobaan aplikasi. |
PurchaseDate | Tanggal saat aplikasi diperoleh. |
Elemen ProductReceipt
Elemen ini berisi informasi tentang pembelian dalam aplikasi untuk aplikasi saat ini.
ProductReceipt memiliki atribut berikut.
Atribut | Deskripsi |
---|---|
Id | Mengidentifikasi pembelian. |
AppId | Mengidentifikasi aplikasi tempat pengguna melakukan pembelian. |
ProductId | Mengidentifikasi produk yang dibeli. |
ProductType | Menentukan jenis produk. Saat ini hanya mendukung nilai Durable. |
PurchaseDate | Tanggal pembelian terjadi. |
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk