in-app-purchase scarica - Come verificare l'acquisto per l'app Android sul lato server(google play in app billing v3)




store cellulari (5)

Puoi provare a utilizzare Acquisti.subscrizioni: ottieni lato server. Prende packageName, subscriptionId e token come parametri e richiede l' authorization .

Controlla se l'acquisto dell'abbonamento di un utente è valido e restituisce la sua scadenza.

In caso di esito positivo, questo metodo restituisce una risorsa Acquisti.subscrizioni nel corpo della risposta.

Ho una semplice app (richiede login utente con account). Fornisco alcune funzionalità premium per gli utenti a pagamento, ad esempio più contenuti di notizie.

Devo registrare se l'utente ha acquistato questo articolo nel mio database del server. Quando fornisco il contenuto dei dati al dispositivo dell'utente, posso controllare lo stato dell'utente e fornire contenuti diversi per l'utente a pagamento.

Ho controllato l'esempio Trivialdrive ufficiale fornito da Google, non fornisce alcun codice di esempio per la verifica lato server, ecco le mie domande.

  1. Ho trovato che l'esempio utilizza la chiave pubblica della mia app per verificare l'acquisto, non sembra buono, penso di poter spostare il processo di verifica sul mio server combinato con le credenziali di accesso dell'utente per vedere se l'utente ha completato l'acquisto e quindi aggiornare il database.
  2. Inoltre c'è l' API di acquisto che posso usare per interrogare, ciò di cui ho bisogno è di passare il buyToken dell'utente nel server.

Non sono sicuro di quale metodo dovrei adottare per verificare l'acquisto dell'utente e segnare lo stato dell'utente nel mio database, forse entrambi?

E temo che ci sia una situazione, se un utente ha acquistato questo oggetto da Google Play, ma per qualche motivo, proprio in quel momento, quando la mia app ha avviato la verifica sul mio server, la connessione di rete è inattiva o il mio server è inattivo , l'utente ha appena pagato i soldi in Google Play ma non ho registrato l'acquisto sul mio server? Cosa dovrei fare, come posso affrontare questa situazione.


Rispondo a questa preoccupazione

la connessione di rete è inattiva o il mio server è inattivo, l'utente ha appena pagato i soldi in Google Play ma non ho registrato l'acquisto sul mio server? Cosa dovrei fare, come posso affrontare questa situazione.

La situazione è:

L'utente acquista la voce "abc" utilizzando il servizio di riproduzione di Google -> restituisce OK -> non riesce a verificare con il server per alcuni motivi, ad esempio nessuna connessione Internet.

La soluzione è:

Sul lato client, prima di mostrare il pulsante "Google Wallet", controlli se l'elemento "abc" è già di proprietà.

  • in caso affermativo, verificare di nuovo con il server
  • se no, mostra il pulsante "Google Wallet".

Acquisto acquisto = mInventory.getPurchase ('abc');

if (purchase != null) // Verify with server 

else // show Google Wallet button

https://developer.android.com/google/play/billing/billing_reference.html#getSkuDetails


Esempio completo di utilizzo della libreria client dell'API di Google per PHP :

  1. Imposta il tuo progetto Google e accedi a Google Play per il tuo account di servizio come descritto nella risposta di Marc qui https://.com/a/35138885/1046909 .

  2. Installa la libreria: https://developers.google.com/api-client-library/php/start/installation .

  3. Ora sei in grado di verificare la tua ricevuta nel seguente modo:

    $client = new \Google_Client();
    $client->setAuthConfig('/path/to/service/account/credentials.json');
    $client->addScope('https://www.googleapis.com/auth/androidpublisher');
    $service = new \Google_Service_AndroidPublisher($client);
    $purchase = $service->purchases_subscriptions->get($packageName, $productId, $token);
    

    Dopo questo acquisto $ è l'istanza di Google_Service_AndroidPublisher_SubscriptionPurchase

    $purchase->getAutoRenewing();
    $purchase->getCancelReason();
    ...
    

La documentazione su questo è confusa e stranamente dettagliata con le cose che sono quasi irrilevanti lasciando la documentazione veramente importante quasi scollegata e super difficile da trovare. Questo dovrebbe funzionare alla perfezione sulla maggior parte della piattaforma server più popolare che può eseguire le librerie client di google api, tra cui Java, Python, .Net e NodeJS, tra gli altri. Nota: ho testato solo il client API Python come mostrato di seguito.

Passaggi necessari:

  1. Crea un progetto API, dal link Accesso API nella tua console di Google Play

  2. Crea un nuovo account di servizio, salva la chiave privata JSON che viene generata. Dovrai portare questo file sul tuo server.

  3. Premi Fine nella sezione dell'account di servizio della console di gioco per aggiornare e quindi concedere l'accesso all'account del servizio

  4. Vai a ottenere una libreria client google api per la tua piattaforma server da https://developers.google.com/api-client-library

  5. Utilizza la libreria client della tua particolare piattaforma per creare un'interfaccia di servizio e leggere direttamente il risultato della verifica dell'acquisto.

Non è necessario preoccuparsi degli ambiti di autorizzazione, effettuare chiamate di richieste personalizzate, aggiornare i token di accesso, ecc. La libreria del client API si occupa di tutto. Ecco un esempio di utilizzo della libreria Python per verificare un abbonamento:

Innanzitutto, installa il client API di Google nel tuo pipenv in questo modo:

$ pipenv install google-api-python-client

Quindi è possibile impostare le credenziali del client API utilizzando il file json della chiave privata per autenticare l'account del servizio.

credentials = service_account.Credentials.from_service_account_file("service_account.json")

Ora è possibile verificare direttamente gli acquisti di sottoscrizione o gli acquisti di prodotti tramite la libreria.

#Build the "service" interface to the API you want
service = googleapiclient.discovery.build("androidpublisher", "v3", credentials=credentials)

#Use the token your API got from the app to verify the purchase
result = service.purchases().subscriptions().get(packageName="your.app.package.id", subscriptionId="sku.name", token="token-from-app").execute()
#result is a python object that looks like this ->
# {'kind': 'androidpublisher#subscriptionPurchase', 'startTimeMillis': '1534326259450', 'expiryTimeMillis': '1534328356187', 'autoRenewing': False, 'priceCurrencyCode': 'INR', 'priceAmountMicros': '70000000', 'countryCode': 'IN', 'developerPayload': '', 'cancelReason': 1, 'orderId': 'GPA.1234-4567-1234-1234..5', 'purchaseType': 0}

La documentazione per l'interfaccia di servizio della piattaforma per l'API dello sviluppatore di giochi non è collegata in modo facile da trovare, per alcuni è decisamente difficile da trovare . Ecco i link per le piattaforme popolari che ho trovato:

Python | Java | .NET | PHP | NodeJS (Github TS) | Vai (Github JSON)


C'è del codice che devi aggiungere: in pratica devi implementare una transizione personalizzata. Ma la maggior parte del codice può essere riutilizzata. Inserirò il codice su github come riferimento, ma i passaggi necessari sono:

SecondAcvitiy Crea la tua transizione personalizzata:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

    Transition transition = new CircularReveal();
    transition.setInterpolator(new LinearInterpolator());

    getWindow().setSharedElementEnterTransition(transition);
}

CircularReveal i limiti della vista di acquisizione (valori iniziali e finali) e forniscono due animazioni, la prima quando è necessario animare la visualizzazione circolare dell'immagine a quella grande, la seconda al contrario.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class CircularReveal extends Transition {

    private static final String BOUNDS = "viewBounds";

    private static final String[] PROPS = {BOUNDS};

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    private void captureValues(TransitionValues values) {
        View view = values.view;
        Rect bounds = new Rect();
        bounds.left = view.getLeft();
        bounds.right = view.getRight();
        bounds.top = view.getTop();
        bounds.bottom = view.getBottom();

        values.values.put(BOUNDS, bounds);
    }

    @Override
    public String[] getTransitionProperties() {
        return PROPS;
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
        if (startValues == null || endValues == null) {
            return null;
        }

        Rect startRect = (Rect) startValues.values.get(BOUNDS);
        final Rect endRect = (Rect) endValues.values.get(BOUNDS);

        final View view = endValues.view;

        Animator circularTransition;
        if (isReveal(startRect, endRect)) {
            circularTransition = createReveal(view, startRect, endRect);
            return new NoPauseAnimator(circularTransition);
        } else {
            layout(startRect, view);

            circularTransition = createConceal(view, startRect, endRect);
            circularTransition.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    view.setOutlineProvider(new ViewOutlineProvider() {
                        @Override
                        public void getOutline(View view, Outline outline) {
                            Rect bounds = endRect;
                            bounds.left -= view.getLeft();
                            bounds.top -= view.getTop();
                            bounds.right -= view.getLeft();
                            bounds.bottom -= view.getTop();
                            outline.setOval(bounds);
                            view.setClipToOutline(true);
                        }
                    });
                }
            });
            return new NoPauseAnimator(circularTransition);
        }
    }

    private void layout(Rect startRect, View view) {
        view.layout(startRect.left, startRect.top, startRect.right, startRect.bottom);
    }

    private Animator createReveal(View view, Rect from, Rect to) {

        int centerX = from.centerX();
        int centerY = from.centerY();
        float finalRadius = (float) Math.hypot(to.width(), to.height());

        return ViewAnimationUtils.createCircularReveal(view, centerX, centerY,
            from.width()/2, finalRadius);
    }

    private Animator createConceal(View view, Rect from, Rect to) {

        int centerX = to.centerX();
        int centerY = to.centerY();
        float initialRadius = (float) Math.hypot(from.width(), from.height());

        return ViewAnimationUtils.createCircularReveal(view, centerX, centerY,
            initialRadius, to.width()/2);
    }

    private boolean isReveal(Rect startRect, Rect endRect) {
        return startRect.width() < endRect.width();
    }
}




android in-app-purchase in-app-billing