Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Varje Microsoft Store-transaktion som resulterar i ett lyckat produktköp kan eventuellt returnera ett transaktionskvitto. Det här kvittot innehåller information om den angivna produkten och den ekonomiska kostnaden för kunden.
Att ha åtkomst till den här informationen stöder scenarier där din app måste verifiera att en användare har köpt din app eller har gjort tilläggsköp (kallas även i appprodukt eller IAP) från Microsoft Store. Tänk dig till exempel ett spel som erbjuder nedladdat innehåll. Om användaren som har köpt spelinnehållet vill spela upp det på en annan enhet måste du kontrollera att användaren redan äger innehållet. Så här gör du.
Viktigt!
Den här artikeln visar hur du använder medlemmar i Windows.ApplicationModel.Store namnrymd för att hämta och verifiera ett kvitto för ett köp i appen. Om du använder Windows.Services.Store namnrymd för köp i appen (introduceras i Windows 10, version 1607 och är tillgänglig för projekt som är inriktade på Windows 10 Anniversary Edition (10.0; Build 14393) eller en senare version i Visual Studio) tillhandahåller inte det här namnområdet något API för att hämta inköpskvitton för köp i appen. Du kan dock använda en REST-metod i Microsoft Store-samlings-API:et för att hämta data för en inköpstransaktion. Mer information finns i Kvitton för köp i appen.
Begära ett kvitto
Windows.ApplicationModel.Store-namnområdet har stöd för flera sätt att få ett kvitto:
- När du gör ett köp med hjälp av CurrentApp.RequestAppPurchaseAsync eller CurrentApp.RequestProductPurchaseAsync (eller någon av de andra överlagringarna av den här metoden), innehåller returvärdet kvittot.
- Du kan anropa metoden CurrentApp.GetAppReceiptAsync för att hämta aktuell kvittoinformation för din app och eventuella tillägg i din app.
Ett appkvitto ser ut ungefär så här.
Anmärkning
Det här exemplet är formaterat för att göra XML-koden läsbar. Verkliga appkvitton innehåller inte blanksteg mellan element.
<Receipt Version="1.0" ReceiptDate="2012-08-30T23:10:05Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="0a0a0a0a-1111-bbbb-2222-3c3c3c3c3c3c">
<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>
Ett produktkvitto ser ut så här.
Anmärkning
Det här exemplet är formaterat för att göra XML-koden läsbar. Riktiga kvitton för produkter innehåller inte mellanslag mellan elementen.
<Receipt Version="1.0" ReceiptDate="2012-08-30T23:08:52Z" CertificateId="b809e47cd0110a4db043b3f73e83acd917fe1336" ReceiptDeviceId="0a0a0a0a-1111-bbbb-2222-3c3c3c3c3c3c">
<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>
Du kan använda något av dessa kvittoexempel för att testa verifieringskoden. Mer information om innehållet i kvittot finns i -elementet och attributbeskrivningarna.
Validera ett kvitto
För att verifiera ett kvittos äkthet behöver du ditt serverdelssystem (en webbtjänst eller något liknande) för att kontrollera kvittots signatur med hjälp av det offentliga certifikatet. Om du vill hämta det här certifikatet använder du URL:en https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60
, där CertificateId
är värdet CertificateId i kvittot.
Här är ett exempel på valideringsprocessen. Den här koden körs i ett .NET Framework-konsolprogram som innehåller en referens till sammansättningen 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());
}
}
}
}
Beskrivningar av element och attribut för ett kvitto
I det här avsnittet beskrivs elementen och attributen i ett kvitto.
Kvittoelement
Rotelementet i den här filen är elementet Receipt, som innehåller information om app- och appköp. Det här elementet innehåller följande delar.
Komponent | Krävs | Kvantitet | Beskrivning |
---|---|---|---|
AppReceipt | Nej | 0 eller 1 | Innehåller köpinformation för den aktuella appen. |
Produktkvitto | Nej | 0 eller mer | Innehåller information om ett köp i appen för den aktuella appen. |
Underskrift | Ja | 1 | Det här elementet är en standardkonstruktion XML-DSIG. Den innehåller ett SignatureValue--element som innehåller signaturen som du kan använda för att verifiera kvittot och ett SignInfo--element. |
Kvitto har följande attribut.
Egenskap | Beskrivning |
---|---|
Version: | Versionsnumret för kvittot. |
CertifikatId | Certifikatets tumavtryck som användes för att signera kvittot. |
Kvittodatum | Datum då kvittot signerades och laddades ned. |
KvittoEnhetId | Identifierar den enhet som används för att begära det här kvittot. |
AppReceipt-elementet
Det här elementet innehåller köpinformation för den aktuella appen.
AppReceipt har följande attribut.
Egenskap | Beskrivning |
---|---|
ID | Identifierar köpet. |
AppId | Värdet Paketfamiljenamn som operativsystemet använder för appen. |
Licenstyp | Fullständigom användaren har köpt den fullständiga versionen av appen. Utvärderingsversion, om användaren laddade ned en utvärderingsversion av appen. |
PurchaseDate | Datum då appen förvärvades. |
Produktkvittoelement
Det här elementet innehåller information om ett köp i appen för den aktuella appen.
ProductReceipt har följande attribut.
Egenskap | Beskrivning |
---|---|
ID | Identifierar köpet. |
AppId | Identifierar den app genom vilken användaren gjorde köpet. |
Produkt-ID | Identifierar den köpta produkten. |
Produkttyp | Avgör produkttypen. Stöder för närvarande endast värdet Durable. |
PurchaseDate | Datum då köpet inträffade. |