'GET', 'timeout' => 15, 'headers' => array( 'Authorization' => 'Bearer ' . $secret, 'Cache-Control' => 'no-cache', ), ) ); if ( is_wp_error( $response ) ) { return new WP_Error( 'dst_key_unreachable', __( 'Could not reach Paystack to verify the key.', 'dst-donate' ) ); } $code = (int) wp_remote_retrieve_response_code( $response ); if ( 401 === $code || 403 === $code ) { return new WP_Error( 'dst_key_invalid', __( 'Paystack rejected this secret key.', 'dst-donate' ) ); } if ( $code >= 200 && $code < 300 ) { return true; } // Any other status: treat as not verifiable rather than invalid. return new WP_Error( 'dst_key_unreachable', __( 'Paystack returned an unexpected response while verifying the key.', 'dst-donate' ) ); } /** * Initialize a transaction. The body is filterable so add-ons can attach a * plan (recurring), a split, or metadata without touching the core. * * @param array $params email, amount (subunit int), reference, currency, callback_url, metadata, plan, split_code, subaccount... * @return array|WP_Error */ public static function initialize_transaction( $params ) { $body = array( 'email' => $params['email'], 'amount' => (int) $params['amount'], 'reference' => $params['reference'], 'currency' => $params['currency'], 'callback_url' => $params['callback_url'], 'metadata' => isset( $params['metadata'] ) ? $params['metadata'] : array(), ); // Pass-through keys add-ons may set. foreach ( array( 'plan', 'split_code', 'subaccount', 'transaction_charge', 'bearer' ) as $k ) { if ( isset( $params[ $k ] ) && '' !== $params[ $k ] ) { $body[ $k ] = $params[ $k ]; } } return self::request( 'POST', '/transaction/initialize', $body ); } /** * Verify a transaction by reference. * * @param string $reference Reference. * @return array|WP_Error */ public static function verify_transaction( $reference ) { return self::request( 'GET', '/transaction/verify/' . rawurlencode( $reference ) ); } /** * Low-level request. Never logs the secret key. * * @param string $method HTTP method. * @param string $path Path. * @param array $body Body for POST. * @return array|WP_Error */ public static function request( $method, $path, $body = array() ) { $secret = DST_Donate_Settings::secret_key(); if ( '' === $secret ) { return new WP_Error( 'dst_no_secret', __( 'Paystack secret key is not configured.', 'dst-donate' ) ); } $args = array( 'method' => $method, 'timeout' => 30, 'headers' => array( 'Authorization' => 'Bearer ' . $secret, 'Content-Type' => 'application/json', 'Cache-Control' => 'no-cache', ), ); if ( 'POST' === $method && ! empty( $body ) ) { $args['body'] = wp_json_encode( $body ); } $response = wp_remote_request( self::API_BASE . $path, $args ); if ( is_wp_error( $response ) ) { return $response; } $code = wp_remote_retrieve_response_code( $response ); $data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( null === $data ) { return new WP_Error( 'dst_bad_response', __( 'Unreadable response from Paystack.', 'dst-donate' ) ); } if ( $code < 200 || $code >= 300 || empty( $data['status'] ) ) { $message = isset( $data['message'] ) ? $data['message'] : __( 'Paystack request failed.', 'dst-donate' ); return new WP_Error( 'dst_api_error', $message, array( 'http_code' => $code ) ); } return $data; } }