# Fizetési átjáró API

> Saját fizetési átjáró írása: regisztrálás, az RCP_Payment_Gateway osztály metódusai, regisztráció feldolgozása, webhookok, tagságok és fizetések kezelése.

A bővítmény teljes fizetési átjáró API-t kínál, amellyel további fizetési szolgáltatókat köthetsz be. Egy átjáró két részből áll: regisztrálni kell, és létre kell hozni az `RCP_Payment_Gateway` osztály egy leszármazottját. Ez egyéni kód, a saját felelősségedre dolgozz vele.

## Az átjáró regisztrálása

Az átjárókat az `rcp_payment_gateways` szűrő regisztrálja. A szűrő egyetlen paramétert, a `$gateways` tömböt kapja, amely az eddig regisztrált átjárókat tartalmazza. Minden átjáró egy tömb a következő kulcsokkal:

| Kulcs | Jelentése |
| --- | --- |
| `class` | Az `RCP_Payment_Gateway` osztályt kiterjesztő osztály neve. |
| `label` | A fizetési mód felirata, amelyet a vásárló lát. |
| `admin_label` | Az átjáró megnevezése a beállítások képernyőn. |

Az átjáró azonosítója a fő tömbben használt kulcs.

```php
function pw_rcp_register_2checkout_gateway( $gateways ) {

	$gateways['2checkout'] = array(
		'label'       => '2Checkout',
		'admin_label' => '2Checkout Redirect',
		'class'       => 'RCP_Payment_Gateway_2Checkout'
	);

	return $gateways;
}
add_filter( 'rcp_payment_gateways', 'pw_rcp_register_2checkout_gateway' );
```

## Az alaposztály metódusai

| Metódus | Kötelező? | Mire való |
| --- | --- | --- |
| `init()` | igen | Az átjáró tulajdonságainak beállítása, például API-kulcsok és végpontok. |
| `process_signup()` | igen | Akkor fut le, amikor a regisztrációs űrlapot elküldik és minden ellenőrzés sikerült. Itt történik a fizetés és/vagy az előfizetés létrehozása az átjárónál. |
| `process_webhooks()` | nem | A fizetési szolgáltató webhookjainak felismerése és feldolgozása, jellemzően a megújításokhoz. |
| `scripts()` | nem | Az átjáróhoz szükséges JS/CSS betöltése vagy kiírása. |
| `fields()` | nem | Átjáró-specifikus mezők kiírása a regisztrációs űrlapon. |
| `validate_fields()` | nem | Az átjáró saját mezőinek ellenőrzése. |

Egy átjárónak csak az `init()` és a `process_signup()` kötelező, de a legtöbb a többit is használja.

## Az osztály inicializálása (`init()`)

Az `init()` főleg arra való, hogy megadd, milyen funkciókat támogat az átjáró, és hogy lekérdezd a teszt (sandbox) módot.

```php
public function init() {
	global $rcp_options;

	$this->supports[] = 'one-time';
	$this->supports[] = 'recurring';
	$this->supports[] = 'fees';
	$this->supports[] = 'trial';

	if ( $this->test_mode ) {
		$this->api_login_id          = isset( $rcp_options['authorize_test_api_login'] ) ? sanitize_text_field( $rcp_options['authorize_test_api_login'] ) : '';
		$this->transaction_key       = isset( $rcp_options['authorize_test_txn_key'] ) ? sanitize_text_field( $rcp_options['authorize_test_txn_key'] ) : '';
		$this->transaction_signature = isset( $rcp_options['authorize_test_signature_key'] ) ? sanitize_text_field( $rcp_options['authorize_test_signature_key'] ) : '';
	} else {
		$this->api_login_id          = isset( $rcp_options['authorize_api_login'] ) ? sanitize_text_field( $rcp_options['authorize_api_login'] ) : '';
		$this->transaction_key       = isset( $rcp_options['authorize_txn_key'] ) ? sanitize_text_field( $rcp_options['authorize_txn_key'] ) : '';
		$this->transaction_signature = isset( $rcp_options['authorize_signature_key'] ) ? sanitize_text_field( $rcp_options['authorize_signature_key'] ) : '';
	}

	require_once RCP_ANET_PATH . 'vendor/autoload.php';
}
```

A példa egyszeri fizetést, ismétlődő fizetést, beiratkozási díjat és ingyenes próbaidőt jelez támogatottként. A teszt és az éles API-kulcsok más-más tulajdonságokba kerülnek, végül betöltődik az átjáró SDK-ja.

## A regisztráció feldolgozása (`process_signup()`)

Itt történik a munka oroszlánrésze: az átjáró API-jának hívása vagy átirányítás egy külső fizetési oldalra. A példában átirányítunk egy külső oldalra:

```php
public function process_signup() {

	// A lekérdezési paraméterek összeállítása.
	$args = array(
		'price'        => $this->initial_amount,   // Kezdő ár + beiratkozási díj - kedvezmények. A szint alapára: $this->amount.
		'description'  => $this->subscription_name, // A tagsági szint neve.
		'custom'       => $this->membership->get_id(), // A tagság azonosítója egyedi azonosítóként.
		'email'        => $this->email,             // A felhasználó e-mail-címe.
		'return'       => $this->return_url         // A sikeres regisztráció utáni cím.
	);

	// Automatikus megújítás beállítása, ha kell.
	if ( $this->auto_renew ) {
		$args['interval']       = $this->length_unit;  // month, day, year
		$args['interval_count'] = $this->length;       // 1, 2, 3 ...
	}

	// Átirányítás a külső fizetési oldalra.
	wp_redirect( add_query_arg( $args, 'http://paymentpage.com/api/' ) );
	exit;
}
```

A `process_signup()` fontos teendői:

1. Ha az `$this->auto_renew` hamis, egyszeri tranzakciót kell feldolgozni az `$this->initial_amount` összeggel.
2. Ha az `$this->auto_renew` igaz, ismétlődő előfizetést kell létrehozni az átjárónál. A mai terhelés az `$this->initial_amount`, a megújításoké az `$this->amount`. Ha az előfizetés létrejött, mentsd el az átjáró előfizetés-azonosítóját a tagságba:

   ```php
   $this->membership->set_gateway_subscription_id( 'gateway_subscription_id_goes_here' );
   ```

3. Amikor a kezdő fizetés kifizetettnek bizonyul, frissítsd a függőben lévő fizetésrekordot: add meg a `transaction_id` értéket, és állítsd az állapotot `complete`-re:

   ```php
   $rcp_payments_db->update( $this->payment->id, array(
   	'transaction_id' => 'your_transaction_id_here',
   	'status'         => 'complete'
   ) );
   ```

### Az alaposztály hasznos tulajdonságai

| Tulajdonság | Jelentése |
| --- | --- |
| `$supports` | A támogatott funkciók tömbje. |
| `$email` | A vásárló e-mail-címe. |
| `$user_id` | A vásárló felhasználói azonosítója. |
| `$user_name` | A vásárló felhasználóneve. |
| `$currency` | A kiválasztott pénznem kódja (például „USD”). |
| `$amount` | Az ismétlődő előfizetés összege, az egyszeri díjak és kedvezmények nélkül. |
| `$initial_amount` | Az első fizetés összege, az egyszeri beiratkozási díjakkal, kedvezményekkel és jóváírásokkal együtt. |
| `$discount` | A fizetésből levont kedvezmény teljes összege. |
| `$length` | A tagság időtartama. |
| `$length_unit` | Az időtartam egysége (day, month vagy year). |
| `$signup_fee` | Az első fizetésre rakódó beiratkozási díj. Már benne van az `$initial_amount` értékében. |
| `$subscription_key` | A tagság egyedi előfizetési kulcsa. |
| `$subscription_id` | Annak a tagsági szintnek az azonosítója, amelyre a vásárló feliratkozik. |
| `$subscription_name` | Annak a tagsági szintnek a neve, amelyre a vásárló feliratkozik. |
| `$auto_renew` | Ismétlődő előfizetésről van-e szó. |
| `$return_url` | A sikeres regisztráció utáni átirányítás címe. |
| `$test_mode` | Az oldal teszt (sandbox) módban van-e. |
| `$payment` | A tranzakció fizetésobjektuma. Az átjáróba érkezéskor „pending” állapotú, sikeres fizetés után „complete”-re kell állítani. |
| `$customer` | A felhasználó `RCP_Customer` objektuma. |
| `$membership` | A függőben lévő tagságrekord `RCP_Membership` objektuma. |
| `$subscription_start_date` | Az előfizetés kezdete MySQL formátumban. Alapból üres (ma kezdődik). Ha az első fizetés ingyenes (próbaidő vagy 100% egyszeri kedvezmény), az első fizetés napja lesz. |

## Mezők hozzáadása a regisztrációs űrlaphoz (`fields()`)

A `fields()` visszatérési értékét a bővítmény automatikusan az űrlap végére fűzi, amikor az átjárót kiválasztják. Ez csak átjáró-specifikus mezőkre való. Sok átjárónak bankkártya-űrlap kell, ezt a beépített `card-form` sablonnal adhatod hozzá:

```php
public function fields() {
	ob_start();
	rcp_get_template_part( 'card-form' );
	return ob_get_clean();
}
```

Az `rcp_get_template_part()` a sablonfájl-rendszerrel dolgozik, lásd a sablonfájlok fejezetét. A `card-form` a bővítmény alap bankkártya-sablonja. További mezőket egyszerűen a `fields()` metódusba írhatsz.

## Mezők ellenőrzése (`validate_fields()`)

A hozzáadott mezők ellenőrzésére a `validate_fields()` való. Hibaüzenetet az `add_error()` metódussal rögzítesz, ez megjelenik az űrlapon, és megakadályozza a beküldést.

```php
public function validate_fields() {
	if ( empty( $_POST['rcp_card_number'] ) ) {
		$this->add_error( 'missing_card_number', __( 'The card number you have entered is invalid', 'rcp' ) );
	}
	if ( empty( $_POST['rcp_card_cvc'] ) ) {
		$this->add_error( 'missing_card_code', __( 'The security code you have entered is invalid', 'rcp' ) );
	}
	if ( empty( $_POST['rcp_card_zip'] ) ) {
		$this->add_error( 'missing_card_zip', __( 'The zip / postal code you have entered is invalid', 'rcp' ) );
	}
	if ( empty( $_POST['rcp_card_name'] ) ) {
		$this->add_error( 'missing_card_name', __( 'The card holder name you have entered is invalid', 'rcp' ) );
	}
	if ( empty( $_POST['rcp_card_exp_month'] ) ) {
		$this->add_error( 'missing_card_exp_month', __( 'The card expiration month you have entered is invalid', 'rcp' ) );
	}
	if ( empty( $_POST['rcp_card_exp_year'] ) ) {
		$this->add_error( 'missing_card_exp_year', __( 'The card expiration year you have entered is invalid', 'rcp' ) );
	}
}
```

## Webhookok feldolgozása (`process_webhooks()`)

A webhook értesít arról, hogy megtörtént egy előfizetési fizetés, vagy a kereskedői fiókban módosult egy előfizetés. Két részből áll:

1. Meg kell mondani a fizetési szolgáltatónak, melyik címre küldje a webhookot.
2. A bővítményben fel kell dolgozni az érkező kérést.

A cím megadásának módja szolgáltatónként eltér, gyakran egy paraméter az előfizetés létrehozásakor. A PayPal például a `notify_url` paraméterrel dolgozik. A webhook címe nem más, mint az oldal főcíme egy különleges lekérdezési paraméterrel, amellyel megkülönböztethető egy sima oldallekéréstől:

```php
public function process_signup() {
	$args = array(
		'price'        => $this->initial_amount,
		'description'  => $this->subscription_name,
		'custom'       => $this->membership->get_id(),
		'email'        => $this->email,
		'return'       => $this->return_url,
		'notify_url'   => add_query_arg( 'listener', 'my_gateway_id', home_url( 'index.php' ) )
	);
	if ( $this->auto_renew ) {
		$args['interval']       = $this->length_unit;
		$args['interval_count'] = $this->length;
	}
	wp_redirect( add_query_arg( $args, 'http://paymentpage.com/api/' ) );
	exit;
}
```

A feldolgozás a `process_webhooks()` metódusban történik:

```php
public function process_webhooks() {
	// Webhook-e a kérés?
	if ( ! isset( $_GET['listener'] ) || strtoupper( $_GET['listener'] ) != 'my_gateway_id' ) {
		return;
	}
	// Itt olvasd ki a webhook adatait a $_POST tömbből.
}
```

A webhook alatt ellenőrizni és tisztítani kell az adatokat, majd a szükséges módosításokat el kell végezni a vásárló fiókján. Jellemző teendők:

- fiók aktiválása,
- előfizetés megújítása,
- fizetés rögzítése,
- előfizetés lemondása.

### Példa: a PayPal Express webhookja

A beépített PayPal Express átjáró így dolgozza fel az értesítéseket. Az átjáró azonosítója az `EIPN` listener. A lépések:

1. A `listener` ellenőrzése, naplózás az `rcp_log()` függvénnyel.
2. A POST-adatok bekérése az `rcp_ipn_post` szűrőn át (így módosítható).
3. A tagság megkeresése: előbb a `recurring_payment_id` alapján (`rcp_get_membership_by( 'gateway_subscription_id', … )`), ennek hiányában a `custom` mezőben tárolt tagságazonosítóval (`rcp_get_membership()`). Ha nincs tagság, a feldolgozás leáll.
4. A tagsági szint és a felhasználó azonosítása, a fizetési adatok tömbjének összeállítása (szint neve, fizetéstípus, előfizetési kulcs, felhasználó, ügyfél, tagság, állapot `complete`, az átjáró azonosítója, összeg, dátum, tranzakcióazonosító).
5. Az `rcp_valid_ipn` akció lefuttatása.
6. A tranzakció típusa (`txn_type`) szerinti ág:
   - `recurring_payment_profile_created`: a kezdő fizetés rögzítése. Ha van függő fizetés, azt frissíti (ez aktiválja a tagságot), különben új fizetést szúr be és megújítja a tagságot. Lefut az `rcp_webhook_recurring_payment_profile_created` és az `rcp_gateway_payment_processed` akció.
   - `recurring_payment`: elmentik az előfizetés azonosítóját. Sikertelen vagy függő fizetésnél jegyzet kerül a tagságra és a feldolgozás megáll. Sikeres fizetésnél `$membership->renew( true )`, a fizetés beszúrása `renewal` tranzakciótípussal, majd az `rcp_ipn_subscr_payment`, `rcp_webhook_recurring_payment_processed` és `rcp_gateway_payment_processed` akciók.
   - `recurring_payment_profile_cancel`: ha a kezdő fizetés sikertelen volt, a tagság visszaáll „pending” állapotba, és lefut az `rcp_registration_failed` és az `rcp_paypal_express_initial_payment_failed` akció. Egyébként (kivéve a lezárult fizetési tervet) a tagság lemondottá válik, de a hozzáférés a futamidő végéig megmarad (`$membership->cancel()`, `rcp_ipn_subscr_cancel`, `rcp_webhook_cancel`).
   - `recurring_payment_failed` és `recurring_payment_suspended_due_to_max_failed_payment`: ha a tagság nem lemondott, „expired” lesz; lefut az `rcp_ipn_subscr_failed` és az `rcp_recurring_payment_failed` akció.
   - `web_accept`: „completed” állapotnál a fizetés beszúrásra kerül (ha a tranzakció még nem létezik); „denied”, „expired”, „failed” vagy „voided” állapotnál az aktív tagság lemondásra kerül.

## Tagságok módosítása

Az átjáró feladata a felhasználói előfizetések módosítása is (lemondás, aktiválás, fizetések). Ehhez az `RCP_Membership` osztály ad segítséget. Példányosítás:

```php
$membership = rcp_get_membership( $membership_id );
```

Ha az átjáró előfizetés-azonosítója megvan, abból is lekérhető a tagság:

```php
$membership = rcp_get_membership_by( 'gateway_subscription_id', 'your_gateway_subscription_id' );
```

Ezután hívhatod az osztály nyilvános metódusait. Megújításkor (például ha beérkezett egy fizetés):

```php
$membership->renew( true );
```

A `true` jelzi, hogy az előfizetés ismétlődő. Ha nem az, `false`-t adj meg. A `renew()` a tagsági szint alapján magától kiszámítja és beállítja a lejárati dátumot. Lemondás:

```php
$membership->cancel();
```

Az első aktiválás általában a függő fizetés teljesítésével történik: ha a függő fizetés állapotát „complete”-re állítod, a hozzá tartozó tagság automatikusan aktiválódik.

## Fizetések rögzítése

Az átjárónak rögzítenie kell a fizetéseket, hogy megjelenjenek a **Restrict → Payments** oldalon. A kezdő fizetést jellemzően a regisztrációkor, az ismétlődőket a webhook feldolgozásakor rögzíted. Ehhez az `RCP_Payments` osztály kell:

```php
$membership = rcp_get_membership( $membership_id );
$payments   = new RCP_Payments();
$args = array(
	'date'             => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ), // A fizetés dátuma
	'subscription'     => $membership->get_membership_level_name(),           // A tagsági szint neve
	'object_id'        => $membership->get_object_id(),                       // A tagsági szint azonosítója
	'payment_type'     => 'Credit Card',                                      // Fizetéstípus (főleg visszamenőleges kompatibilitás miatt)
	'gateway'          => 'my_gateway_id',                                    // Az átjáró azonosítója
	'subscription_key' => $membership->get_subscription_key(),                // Előfizetési kulcs
	'amount'           => $amount,                                            // Teljes összeg
	'subtotal'         => $subtotal,                                          // Részösszeg
	'credits'          => $credits,                                           // Alkalmazott jóváírások
	'fees'             => $fees,                                              // Beiratkozási díjak
	'discount_amount'  => $discount_amount,                                   // Kedvezmény összege
	'discount_code'    => $discount_code,                                     // Használt kedvezménykód
	'user_id'          => $membership->get_customer()->get_user_id(),         // A fizető felhasználó azonosítója
	'customer_id'      => $membership->get_customer()->get_id(),              // Az ügyfél azonosítója
	'membership_id'    => $membership->get_id(),                              // A tagság azonosítója
	'transaction_type' => 'new',                                              // new, upgrade, downgrade vagy renewal
	'transaction_id'   => $transaction_id,                                    // Az átjáró tranzakcióazonosítója
	'status'           => 'complete',                                         // complete, pending vagy failed
);
$payments->insert( $args );
```

Az egyes értékeket neked kell megadnod, általában a webhook POST-adataiból vagy az API válaszából. A függő fizetést is teljesítheted, ha megvan a tagságobjektum, mert a függő fizetés azonosítója ott van eltárolva:

```php
$payments           = new RCP_Payments();
$pending_payment_id = rcp_get_membership_meta( $membership->get_id(), 'pending_payment_id', true );

if ( ! empty( $pending_payment_id ) ) {
	$payment_data = array( 'status' => 'complete' ); // A frissítendő adatok
	$payments->update( absint( $pending_payment_id ), $payment_data );
}
```

Amikor a függő fizetés teljesül, a tagság automatikusan aktiválódik, a vásárló hozzáférést kap, és megkapja az aktív tagság e-mailt (ha be van állítva).
