Utiliser des reçus pour vérifier les achats de produits
Chaque transaction du Microsoft Store qui aboutit à un achat de produit réussi peut éventuellement retourner un reçu de transaction. Ce reçu fournit des informations sur le produit et le coût monétaire pour le client.
L’accès à ces informations prend en charge les scénarios où votre application doit vérifier qu’un utilisateur a acheté votre application ou qu’il a effectué des achats de compléments (également appelés produits in-app ou IAP) à partir du Microsoft Store. Par exemple, imaginez un jeu qui propose du contenu téléchargé. Si l’utilisateur qui a acheté le contenu du jeu veut jouer à ce jeu sur un autre appareil, vous devez vérifier qu’il a bien acheté le contenu. Voici comment procéder.
Important
Cet article explique comment utiliser les membres de l’espace de noms Windows.ApplicationModel.Store pour obtenir et valider un reçu pour un achat dans l’application. Si vous utilisez l’espace de noms Windows.Services.Store pour les achats dans l’application (introduit dans Windows 10 version 1607 et disponible pour les projets qui ciblent Windows 10 Édition anniversaire (10.0 ; Build 14393) ou une version ultérieure dans Visual Studio), cet espace de noms ne fournit pas d’API permettant d’obtenir des reçus d’achat pour les achats dans l’application. Toutefois, vous pouvez utiliser une méthode REST dans l’API de collecte du Microsoft Store pour obtenir des données pour une transaction d’achat. Pour plus d’informations, consultez Reçus d’achats in-app.
Demande d’un reçu
L’espace de noms Windows.ApplicationModel.Store prend en charge plusieurs modes pour obtenir un reçu :
- Lorsque vous effectuez un achat à l’aide de CurrentApp.RequestAppPurchaseAsync ou CurrentApp.RequestProductPurchaseAsync (ou l’une des autres surcharges de cette méthode), la valeur de retour contient le reçu.
- Vous pouvez appeler la méthode CurrentApp.GetAppReceiptAsync pour récupérer les informations du reçu de votre application et des modules complémentaires de votre application.
Un reçu d’application ressemble à ceci.
Notes
Cet exemple est mis en forme pour rendre le code XML lisible. Les reçus d’application réels n’incluent pas d’espaces blancs entre les éléments.
<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>
Un reçu de produit ressemble à ceci.
Notes
Cet exemple est mis en forme pour rendre le code XML lisible. Les reçus de produit réels n’incluent pas d’espaces blancs entre les éléments.
<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>
Vous pouvez utiliser ces exemples de reçu pour tester votre code de validation. Pour plus d’informations sur le contenu du reçu, consultez la description des éléments et des attributs.
Validation d’un reçu
Pour valider l’authenticité d’un reçu, vous avez besoin de votre système dorsal (service web ou autre) afin d’en vérifier la signature à l’aide du certificat public. Pour obtenir ce certificat, utilisez l’URL https://lic.apps.microsoft.com/licensing/certificateserver/?cid=CertificateId%60%60%60
, où CertificateId
est la valeur CertificateId du reçu.
Voici un exemple de ce processus de validation. Ce code s’exécute dans une application de console .NET Framework, qui inclut une référence à l’assemblage 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());
}
}
}
}
Description des éléments et des attributs d’un reçu
Cette section décrit les éléments et attributs d’un reçu.
Élément d’un reçu
L’élément racine de ce fichier est l’élément Receipt, qui contient des informations sur l’application et les achats in-app. Cet élément contient les éléments enfants suivants :
Élément | Obligatoire | Quantité | Description |
---|---|---|---|
AppReceipt | Non | 0 ou 1 | Contient des informations sur l’achat pour l’application actuelle. |
ProductReceipt | Non | 0 ou plus | Contient des informations sur un achat in-app pour l’application actuelle. |
Signature | Oui | 1 | Cet élément est une construction XML-DSIG standard. Il contient un élément SignatureValue qui contient la signature que vous pouvez utiliser pour valider le reçu, un élément SignedInfo. |
L’élément Receipt a les attributs suivants :
Attribut | Description |
---|---|
Version | Numéro de version du reçu. |
Id de certificat | Empreinte de certificat utilisée pour signer le reçu. |
ReceiptDate | Date de signature et de téléchargement du reçu. |
ReceiptDeviceId | Identifie l’appareil utilisé pour demander ce reçu. |
Élément AppReceipt
Cet élément contient des informations sur l’achat pour l’application actuelle.
L’élément AppReceipt a les attributs suivants :
Attribut | Description |
---|---|
Id | Identifie l’achat. |
AppID | Nom de la famille de packages, utilisé par le système d’exploitation pour l’application. |
LicenseType | Full, si l’utilisateur a acheté la version complète de l’application. Trial, si l’utilisateur a téléchargé une version d’évaluation de l’application. |
PurchaseDate | Date d’acquisition de l’application. |
Élément ProductReceipt
Cet élément contient des informations sur un achat in-app pour l’application actuelle.
L’élément ProductReceipt a les attributs suivants :
Attribut | Description |
---|---|
Id | Identifie l’achat. |
AppID | Identifie l’application avec laquelle l’utilisateur a effectué l’achat. |
ProductId | Identifie le produit acheté. |
ProductType | Détermine le type de produit. Actuellement, ne prend en charge que la valeur Durable. |
PurchaseDate | Date à laquelle l’achat a eu lieu. |
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : Tout au long de 2024, nous allons supprimer progressivement GitHub Issues comme mécanisme de commentaires pour le contenu et le remplacer par un nouveau système de commentaires. Pour plus d’informations, consultezEnvoyer et afficher des commentaires pour