Archivos mensuales: mayo 2020

Cómo efectuar pagos mediante Apple Pay y Stripe en Ionic

Lo primero de todo vas a necesitar el siguiente plugin para poder llamar a las funcionalidades de Apple Pay:

https://ionicframework.com/docs/native/apple-pay

Después tienes que ir al panel de Stripe y activar el método de pago con Apple Pay, desde ahí te deberás de bajar un CSR (Certificate Singing Request) que deberás de usar en el Portal de desarrollador de Apple.

Desde el porta de desarrollador de Apple deberas de crear un Merchant ID:

Y luego dentro de ese Merchant ID deberás de usar el CSR para crear un certificado en el apartado de Apple Pay Payment Processing Certificate:

Después te bajas el certificado y lo instalas haciendo doble click sobre él.

Con eso ya tienes preparado el entorno de Stripe y Apple para poder usar Apple Pay.

Ahora dentro de la la app, sigues el ejemplo de la página del plugin:

https://github.com/samkelleher/cordova-plugin-applepay

Aquí hay que tener en cuenta que en items, van todos los items que ha comprado el usuarios, más un item más que es el total, es decir que si el usuario ha pagado tres productos, en items tendremos los siguiente:

const items = [
          {
            label: 'producto1',
            amount: 15.95
          },
          {
            label: 'producto2',
            amount: 9.95
          },
          {
            label: 'producto2',
            amount: 7.95
          },
          {
            label: 'MI EMPRESA/APP O LO QUE SEA',
            amount: 33.85
          }
        ];

El final al usuario se le cobrará el importe del último item, el resto forma parte del desglose de la compra, y MUY IMPORTANTE, el último item tiene que tener el nombre de la app o de la empresa o del servicio al que hay que hacer el pago.

Después y siguiendo el ejemplo del plugin, en la respuesta a la llamada makePaymentRequest tendremos el token de Apple Pay que ira dentro del parámetro paymentData de la respuesta e irá codificado en base64.

Cogiendo la respuesta y utilizando la siguiente función:

async getTokenFromPaymentData(payment: any) {
    const payload: any = {
      pk_token: atob(payment.paymentData),
      muid: UUID_DEL_DISPOSITIVO,
      time_on_page: '0'
    };

    if (payment.paymentMethodDisplayName) {
      payload.pk_token_instrument_name = payment.paymentMethodDisplayName;
    }

    if (payment.paymentMethodNetwork) {
      payload.pk_token_payment_network = payment.paymentMethodNetwork;
    }

    if (payment.transactionIdentifier) {
      payload.pk_token_transaction_id = payment.transactionIdentifier;
    }

    const response = await fetch('https://api.stripe.com/v1/tokens', {
      method: 'POST',
      headers: {
        'Stripe-Version': '2015-10-12',
        Authorization: `Bearer pk_live_XXXXXXXXXXXXXXXXXXXXXX`,
      },
      body: new URLSearchParams(payload),
    });

    const data = await response.json();
    const token = data.id;

    return token;
  }

De la siguiente manera:

...
}).then(async(paymentResponse) => {
           
   const token = await this.getTokenFromPaymentData(paymentResponse);
...

Obtendremos el token de Stripe, lo mandaremos al servidor donde procesamos los pagos y realizaremos el pago a través de la API de Stripe:

\Stripe\Stripe::setApiKey('sk_live_XXXXXXXXXXXXXXXXXX');

\Stripe\Charge::create([
  'amount' => 3385,
  'currency' => 'eur',
  'source' => 'token',
  'description' => 'Compra X',
]);

Ionic 4+ ErrorHandler, Cómo enviar los errores a un servicio externo

Cuando tu app da error y se ejecuta en dispositivos en los que no tienes acceso a los logs, esta solución te puede solucionar parte del camino para resolver el error.

Primero creamos el servicio que enviara el error a un servidor o servicio externo, en este ejemplo yo hago una petición HTTP a una URL externa, pero puede ser todo lo complejo que quieras:

error-log-service.ts

import { Injectable } from '@angular/core';

import { HttpClient, HttpHeaders} from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ErrorLogService {

  constructor(
    private http: HttpClient
  ){ }

  sendError(error) {

    const headers: HttpHeaders = new HttpHeaders({
      'Content-Type': 'application/json'
    });

    const requestOptions = { headers };

    const url = 'https://www.javray.com/log.php';

    const body: any = {};

    body.message = error.message || 'N/A';
    body.stack = error.stack || 'N/A';

    this.http.post(url, body, requestOptions).subscribe();
  }
}

Después definimos la clase que extenderá ErrorHandler y que hará uso de nuestro servicio:

my-error-handler.ts

import { Injectable, ErrorHandler } from '@angular/core';

import { ErrorLogService } from './error-log.service';

@Injectable({
  providedIn: 'root'
})
export class MyErrorHandler extends ErrorHandler {

  constructor(private errorLogService: ErrorLogService) {
    super();
  }

  handleError(error) {
    super.handleError(error);
    this.errorLogService.sendError(error);
  }
}

Y por ultimo añadimos MyErrorHandler como provider en la app:

app.module.ts


import { ErrorHandler, NgModule, Injectable } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { HttpClientModule } from '@angular/common/http';

import { MyErrorHandler } from './my-error-handler';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: ErrorHandler, useClass: MyErrorHandler },
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Una implementación sencilla del servicio que recibe los logs sería la siguiente:

log.php


header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Origin: *');

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
        exit(0);
}

$params = json_decode(file_get_contents('php://input'), true);

$fecha = date('Y-m-d H:m:s');

$log = '[' . $fecha . '] ';
$log .= '"' . $params['message'] . '" ';
$log .= $params['stack'];

$fichero = '/var/www/html/logs/' . date('Ymd') . '.log';

file_put_contents($fichero, $log . "\n", FILE_APPEND);

echo json_encode(array('estado' => 'OK'));