diff --git a/.gitignore b/.gitignore index ee9e36f..1d3c42d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /.idea/ /.vscode/ /.phpunit.result.cache +/.phpunit.cache/ /.php-cs-fixer.cache /phpunit.xml /.env diff --git a/composer.json b/composer.json index 0e21047..52f97ab 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ }, "autoload-dev": { "psr-4": { - "Tests\\": "tests/" + "BlindPay\\SDK\\Tests\\": "tests/" } }, "authors": [ @@ -49,4 +49,3 @@ "lint:fix": "pint" } } - diff --git a/tests/Resources/ApiKeys/ApiKeysTest.php b/tests/Resources/ApiKeys/ApiKeysTest.php new file mode 100644 index 0000000..7fc07c9 --- /dev/null +++ b/tests/Resources/ApiKeys/ApiKeysTest.php @@ -0,0 +1,142 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_creates_an_api_key(): void + { + $this->mockResponse([ + 'id' => 'ap_000000000000', + 'token' => 'token', + ]); + + $input = new CreateApiKeyInput( + name: 'test', + permission: Permission::FULL_ACCESS + ); + + $response = $this->blindpay->instances->apiKeys->create($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ap_000000000000', $response->data->id); + $this->assertEquals('token', $response->data->token); + } + + #[Test] + public function it_gets_an_api_key(): void + { + $this->mockResponse([ + 'id' => 'ap_000000000000', + 'token' => 'token', + 'name' => 'test', + 'permission' => 'full_access', + 'ip_whitelist' => ['127.0.0.1'], + 'unkey_id' => 'key_123456789', + 'last_used_at' => '2024-01-01T00:00:00.000Z', + 'instance_id' => 'in_000000000000', + 'created_at' => '2021-01-01', + 'updated_at' => '2021-01-01', + ]); + + $response = $this->blindpay->instances->apiKeys->get('ap_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertEquals('ap_000000000000', $response->data->id); + $this->assertEquals('test', $response->data->name); + $this->assertEquals(Permission::FULL_ACCESS, $response->data->permission); + $this->assertCount(1, $response->data->ipWhitelist); + $this->assertEquals('127.0.0.1', $response->data->ipWhitelist[0]); + } + + #[Test] + public function it_lists_api_keys(): void + { + $this->mockResponse([ + [ + 'id' => 'ap_000000000000', + 'token' => 'token', + 'name' => 'test', + 'permission' => 'full_access', + 'ip_whitelist' => ['127.0.0.1'], + 'unkey_id' => 'key_123456789', + 'last_used_at' => '2024-01-01T00:00:00.000Z', + 'instance_id' => 'in_000000000000', + 'created_at' => '2021-01-01', + 'updated_at' => '2021-01-01', + ], + ]); + + $response = $this->blindpay->instances->apiKeys->list(); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('ap_000000000000', $response->data[0]->id); + } + + #[Test] + public function it_deletes_an_api_key(): void + { + $this->mockResponse(['data' => null]); + + $response = $this->blindpay->instances->apiKeys->delete('ap_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } +} diff --git a/tests/Resources/Available/AvailableTest.php b/tests/Resources/Available/AvailableTest.php new file mode 100644 index 0000000..ec2db72 --- /dev/null +++ b/tests/Resources/Available/AvailableTest.php @@ -0,0 +1,177 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_gets_bank_details_for_a_rail(): void + { + $mockedBankDetails = [ + [ + 'label' => 'Account Type', + 'regex' => '', + 'key' => 'account_type', + 'items' => [ + [ + 'label' => 'Checking', + 'value' => 'checking', + ], + [ + 'label' => 'Savings', + 'value' => 'saving', + ], + ], + 'required' => true, + ], + ]; + + $this->mockResponse($mockedBankDetails); + + $response = $this->blindpay->available->getBankDetails(Rail::PIX); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('Account Type', $response->data[0]->label); + $this->assertEquals('', $response->data[0]->regex); + $this->assertEquals('account_type', $response->data[0]->key); + $this->assertTrue($response->data[0]->required); + $this->assertIsArray($response->data[0]->items); + $this->assertCount(2, $response->data[0]->items); + $this->assertEquals('Checking', $response->data[0]->items[0]->label); + $this->assertEquals('checking', $response->data[0]->items[0]->value); + $this->assertEquals('Savings', $response->data[0]->items[1]->label); + $this->assertEquals('saving', $response->data[0]->items[1]->value); + } + + #[Test] + public function it_gets_available_rails(): void + { + $mockedRails = [ + [ + 'label' => 'Domestic Wire', + 'value' => 'wire', + 'country' => 'US', + ], + [ + 'label' => 'ACH', + 'value' => 'ach', + 'country' => 'US', + ], + [ + 'label' => 'PIX', + 'value' => 'pix', + 'country' => 'BR', + ], + [ + 'label' => 'SPEI', + 'value' => 'spei_bitso', + 'country' => 'MX', + ], + [ + 'label' => 'Transfers 3.0', + 'value' => 'transfers_bitso', + 'country' => 'AR', + ], + [ + 'label' => 'ACH Colombia', + 'value' => 'ach_cop_bitso', + 'country' => 'CO', + ], + [ + 'label' => 'International Swift', + 'value' => 'international_swift', + 'country' => 'US', + ], + [ + 'label' => 'RTP', + 'value' => 'rtp', + 'country' => 'US', + ], + ]; + + $this->mockResponse($mockedRails); + + $response = $this->blindpay->available->getRails(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(8, $response->data); + $this->assertEquals('Domestic Wire', $response->data[0]->label); + $this->assertEquals('wire', $response->data[0]->value); + $this->assertEquals('US', $response->data[0]->country); + $this->assertEquals('ACH', $response->data[1]->label); + $this->assertEquals('ach', $response->data[1]->value); + $this->assertEquals('US', $response->data[1]->country); + $this->assertEquals('PIX', $response->data[2]->label); + $this->assertEquals('pix', $response->data[2]->value); + $this->assertEquals('BR', $response->data[2]->country); + $this->assertEquals('SPEI', $response->data[3]->label); + $this->assertEquals('spei_bitso', $response->data[3]->value); + $this->assertEquals('MX', $response->data[3]->country); + $this->assertEquals('Transfers 3.0', $response->data[4]->label); + $this->assertEquals('transfers_bitso', $response->data[4]->value); + $this->assertEquals('AR', $response->data[4]->country); + $this->assertEquals('ACH Colombia', $response->data[5]->label); + $this->assertEquals('ach_cop_bitso', $response->data[5]->value); + $this->assertEquals('CO', $response->data[5]->country); + $this->assertEquals('International Swift', $response->data[6]->label); + $this->assertEquals('international_swift', $response->data[6]->value); + $this->assertEquals('US', $response->data[6]->country); + $this->assertEquals('RTP', $response->data[7]->label); + $this->assertEquals('rtp', $response->data[7]->value); + $this->assertEquals('US', $response->data[7]->country); + } +} diff --git a/tests/Resources/BankAccounts/BankAccountsTest.php b/tests/Resources/BankAccounts/BankAccountsTest.php new file mode 100644 index 0000000..7d020dd --- /dev/null +++ b/tests/Resources/BankAccounts/BankAccountsTest.php @@ -0,0 +1,626 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_creates_a_pix_bank_account(): void + { + $mockedPixAccount = [ + 'id' => 'ba_000000000000', + 'type' => 'pix', + 'name' => 'PIX Account', + 'pix_key' => '14947677768', + 'created_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedPixAccount); + + $input = new CreatePixInput( + receiverId: 're_000000000000', + name: 'PIX Account', + pixKey: '14947677768' + ); + + $response = $this->blindpay->receivers->bankAccounts->createPix($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('pix', $response->data->type); + $this->assertEquals('PIX Account', $response->data->name); + $this->assertEquals('14947677768', $response->data->pixKey); + } + + #[Test] + public function it_creates_an_argentina_transfers_bank_account(): void + { + $mockedArgentinaTransfersAccount = [ + 'id' => 'ba_000000000000', + 'type' => 'transfers_bitso', + 'name' => 'Argentina Transfers Account', + 'beneficiary_name' => 'Individual full name or business name', + 'transfers_type' => 'CVU', + 'transfers_account' => 'BM123123123123', + 'created_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedArgentinaTransfersAccount); + + $input = new CreateArgentinaTransfersInput( + receiverId: 're_000000000000', + name: 'Argentina Transfers Account', + beneficiaryName: 'Individual full name or business name', + transfersAccount: 'BM123123123123', + transfersType: ArgentinaTransfers::CVU + ); + + $response = $this->blindpay->receivers->bankAccounts->createArgentinaTransfers($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('transfers_bitso', $response->data->type); + $this->assertEquals('Argentina Transfers Account', $response->data->name); + $this->assertEquals('Individual full name or business name', $response->data->beneficiaryName); + $this->assertEquals(ArgentinaTransfers::CVU, $response->data->transfersType); + $this->assertEquals('BM123123123123', $response->data->transfersAccount); + } + + #[Test] + public function it_creates_a_spei_bank_account(): void + { + $mockedSpeiAccount = [ + 'id' => 'ba_000000000000', + 'type' => 'spei_bitso', + 'name' => 'SPEI Account', + 'beneficiary_name' => 'Individual full name or business name', + 'spei_protocol' => 'SPEI', + 'spei_institution_code' => '40002', + 'spei_clabe' => '5482347403740546', + 'created_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedSpeiAccount); + + $input = new CreateSpeiInput( + receiverId: 're_000000000000', + beneficiaryName: 'Individual full name or business name', + name: 'SPEI Account', + speiClabe: '5482347403740546', + speiInstitutionCode: '40002', + speiProtocol: SpeiProtocol::SPEI + ); + + $response = $this->blindpay->receivers->bankAccounts->createSpei($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('spei_bitso', $response->data->type); + $this->assertEquals('SPEI Account', $response->data->name); + $this->assertEquals('Individual full name or business name', $response->data->beneficiaryName); + $this->assertEquals('5482347403740546', $response->data->speiClabe); + $this->assertEquals('40002', $response->data->speiInstitutionCode); + } + + #[Test] + public function it_creates_a_colombia_ach_bank_account(): void + { + $mockedColombiaAchAccount = [ + 'id' => 'ba_000000000000', + 'type' => 'ach_cop_bitso', + 'name' => 'Colombia ACH Account', + 'account_type' => 'checking', + 'ach_cop_beneficiary_first_name' => 'Fernando', + 'ach_cop_beneficiary_last_name' => 'Guzman Alarcón', + 'ach_cop_document_id' => '1661105408', + 'ach_cop_document_type' => 'CC', + 'ach_cop_email' => 'fernando.guzman@gmail.com', + 'ach_cop_bank_code' => '051', + 'ach_cop_bank_account' => '12345678', + 'created_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedColombiaAchAccount); + + $input = new CreateColombiaAchInput( + receiverId: 're_000000000000', + name: 'Colombia ACH Account', + accountType: BankAccountType::CHECKING, + achCopBeneficiaryFirstName: 'Fernando', + achCopBeneficiaryLastName: 'Guzman Alarcón', + achCopDocumentId: '1661105408', + achCopDocumentType: AchCopDocument::CC, + achCopEmail: 'fernando.guzman@gmail.com', + achCopBankCode: '051', + achCopBankAccount: '12345678' + ); + + $response = $this->blindpay->receivers->bankAccounts->createColombiaAch($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('ach_cop_bitso', $response->data->type); + $this->assertEquals('Colombia ACH Account', $response->data->name); + $this->assertEquals(BankAccountType::CHECKING, $response->data->accountType); + $this->assertEquals('Fernando', $response->data->achCopBeneficiaryFirstName); + $this->assertEquals('Guzman Alarcón', $response->data->achCopBeneficiaryLastName); + $this->assertEquals('1661105408', $response->data->achCopDocumentId); + $this->assertEquals(AchCopDocument::CC, $response->data->achCopDocumentType); + $this->assertEquals('fernando.guzman@gmail.com', $response->data->achCopEmail); + $this->assertEquals('051', $response->data->achCopBankCode); + $this->assertEquals('12345678', $response->data->achCopBankAccount); + } + + #[Test] + public function it_creates_an_ach_bank_account(): void + { + $mockedAchAccount = [ + 'id' => 'ba_000000000000', + 'type' => 'ach', + 'name' => 'ACH Account', + 'beneficiary_name' => 'Individual full name or business name', + 'routing_number' => '012345678', + 'account_number' => '1001001234', + 'account_type' => 'checking', + 'account_class' => 'individual', + 'address_line_1' => null, + 'address_line_2' => null, + 'city' => null, + 'state_province_region' => null, + 'country' => null, + 'postal_code' => null, + 'ach_cop_beneficiary_first_name' => null, + 'ach_cop_beneficiary_last_name' => null, + 'ach_cop_document_id' => null, + 'ach_cop_document_type' => null, + 'ach_cop_email' => null, + 'ach_cop_bank_code' => null, + 'ach_cop_bank_account' => null, + 'created_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedAchAccount); + + $input = new CreateAchInput( + receiverId: 're_000000000000', + name: 'ACH Account', + accountClass: AccountClass::INDIVIDUAL, + accountNumber: '1001001234', + accountType: BankAccountType::CHECKING, + beneficiaryName: 'Individual full name or business name', + routingNumber: '012345678' + ); + + $response = $this->blindpay->receivers->bankAccounts->createAch($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('ach', $response->data->type); + $this->assertEquals('ACH Account', $response->data->name); + $this->assertEquals('Individual full name or business name', $response->data->beneficiaryName); + $this->assertEquals('012345678', $response->data->routingNumber); + $this->assertEquals('1001001234', $response->data->accountNumber); + $this->assertEquals(BankAccountType::CHECKING, $response->data->accountType); + $this->assertEquals(AccountClass::INDIVIDUAL, $response->data->accountClass); + $this->assertNull($response->data->addressLine1); + $this->assertNull($response->data->addressLine2); + $this->assertNull($response->data->city); + $this->assertNull($response->data->stateProvinceRegion); + $this->assertNull($response->data->country); + $this->assertNull($response->data->postalCode); + } + + #[Test] + public function it_creates_a_wire_bank_account(): void + { + $mockedWireAccount = [ + 'id' => 'ba_000000000000', + 'type' => 'wire', + 'name' => 'Wire Account', + 'beneficiary_name' => 'Individual full name or business name', + 'routing_number' => '012345678', + 'account_number' => '1001001234', + 'address_line_1' => 'Address line 1', + 'address_line_2' => 'Address line 2', + 'city' => 'City', + 'state_province_region' => 'State/Province/Region', + 'country' => 'US', + 'postal_code' => 'Postal code', + 'created_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedWireAccount); + + $input = new CreateWireInput( + receiverId: 're_000000000000', + name: 'Wire Account', + accountNumber: '1001001234', + beneficiaryName: 'Individual full name or business name', + routingNumber: '012345678', + addressLine1: 'Address line 1', + addressLine2: 'Address line 2', + city: 'City', + stateProvinceRegion: 'State/Province/Region', + country: Country::US, + postalCode: 'Postal code' + ); + + $response = $this->blindpay->receivers->bankAccounts->createWire($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('wire', $response->data->type); + $this->assertEquals('Wire Account', $response->data->name); + $this->assertEquals('Individual full name or business name', $response->data->beneficiaryName); + $this->assertEquals('012345678', $response->data->routingNumber); + $this->assertEquals('1001001234', $response->data->accountNumber); + $this->assertEquals('Address line 1', $response->data->addressLine1); + $this->assertEquals('Address line 2', $response->data->addressLine2); + $this->assertEquals('City', $response->data->city); + $this->assertEquals('State/Province/Region', $response->data->stateProvinceRegion); + $this->assertEquals(Country::US, $response->data->country); + $this->assertEquals('Postal code', $response->data->postalCode); + } + + #[Test] + public function it_creates_an_international_swift_bank_account(): void + { + $mockedInternationalSwiftAccount = [ + 'id' => 'ba_000000000000', + 'type' => 'international_swift', + 'name' => 'International Swift Account', + 'beneficiary_name' => null, + 'address_line_1' => null, + 'address_line_2' => null, + 'city' => null, + 'state_province_region' => null, + 'country' => null, + 'postal_code' => null, + 'swift_code_bic' => '123456789', + 'swift_account_holder_name' => 'John Doe', + 'swift_account_number_iban' => '123456789', + 'swift_beneficiary_address_line_1' => '123 Main Street, Suite 100, Downtown District, City Center CP 12345', + 'swift_beneficiary_address_line_2' => null, + 'swift_beneficiary_country' => 'MX', + 'swift_beneficiary_city' => 'City', + 'swift_beneficiary_state_province_region' => 'District', + 'swift_beneficiary_postal_code' => '11530', + 'swift_bank_name' => 'Banco Regional SA', + 'swift_bank_address_line_1' => '123 Main Street, Suite 100, Downtown District, City Center CP 12345', + 'swift_bank_address_line_2' => null, + 'swift_bank_country' => 'MX', + 'swift_bank_city' => 'City', + 'swift_bank_state_province_region' => 'District', + 'swift_bank_postal_code' => '11530', + 'swift_intermediary_bank_swift_code_bic' => null, + 'swift_intermediary_bank_account_number_iban' => null, + 'swift_intermediary_bank_name' => null, + 'swift_intermediary_bank_country' => null, + 'created_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedInternationalSwiftAccount); + + $input = new CreateInternationalSwiftInput( + receiverId: 're_000000000000', + name: 'International Swift Account', + swiftAccountHolderName: 'John Doe', + swiftAccountNumberIban: '123456789', + swiftBankAddressLine1: '123 Main Street, Suite 100, Downtown District, City Center CP 12345', + swiftBankAddressLine2: null, + swiftBankCity: 'City', + swiftBankCountry: Country::MX, + swiftBankName: 'Banco Regional SA', + swiftBankPostalCode: '11530', + swiftBankStateProvinceRegion: 'District', + swiftBeneficiaryAddressLine1: '123 Main Street, Suite 100, Downtown District, City Center CP 12345', + swiftBeneficiaryAddressLine2: null, + swiftBeneficiaryCity: 'City', + swiftBeneficiaryCountry: Country::MX, + swiftBeneficiaryPostalCode: '11530', + swiftBeneficiaryStateProvinceRegion: 'District', + swiftCodeBic: '123456789', + swiftIntermediaryBankAccountNumberIban: null, + swiftIntermediaryBankCountry: null, + swiftIntermediaryBankName: null, + swiftIntermediaryBankSwiftCodeBic: null + ); + + $response = $this->blindpay->receivers->bankAccounts->createInternationalSwift($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('international_swift', $response->data->type); + $this->assertEquals('International Swift Account', $response->data->name); + $this->assertNull($response->data->beneficiaryName); + $this->assertEquals('123456789', $response->data->swiftCodeBic); + $this->assertEquals('John Doe', $response->data->swiftAccountHolderName); + $this->assertEquals('123456789', $response->data->swiftAccountNumberIban); + $this->assertEquals('123 Main Street, Suite 100, Downtown District, City Center CP 12345', $response->data->swiftBeneficiaryAddressLine1); + $this->assertNull($response->data->swiftBeneficiaryAddressLine2); + $this->assertEquals(Country::MX, $response->data->swiftBeneficiaryCountry); + $this->assertEquals('City', $response->data->swiftBeneficiaryCity); + $this->assertEquals('District', $response->data->swiftBeneficiaryStateProvinceRegion); + $this->assertEquals('11530', $response->data->swiftBeneficiaryPostalCode); + $this->assertEquals('Banco Regional SA', $response->data->swiftBankName); + $this->assertEquals('123 Main Street, Suite 100, Downtown District, City Center CP 12345', $response->data->swiftBankAddressLine1); + $this->assertNull($response->data->swiftBankAddressLine2); + $this->assertEquals(Country::MX, $response->data->swiftBankCountry); + $this->assertEquals('City', $response->data->swiftBankCity); + $this->assertEquals('District', $response->data->swiftBankStateProvinceRegion); + $this->assertEquals('11530', $response->data->swiftBankPostalCode); + $this->assertNull($response->data->swiftIntermediaryBankSwiftCodeBic); + $this->assertNull($response->data->swiftIntermediaryBankAccountNumberIban); + $this->assertNull($response->data->swiftIntermediaryBankName); + $this->assertNull($response->data->swiftIntermediaryBankCountry); + } + + #[Test] + public function it_creates_an_rtp_bank_account(): void + { + $mockedRtpAccount = [ + 'id' => 'ba_JW5ZtlKMlgS1', + 'type' => 'rtp', + 'name' => 'John Doe RTP', + 'beneficiary_name' => 'John Doe', + 'routing_number' => '121000358', + 'account_number' => '325203027578', + 'address_line_1' => 'Street of the fools', + 'address_line_2' => null, + 'city' => 'Fools City', + 'state_province_region' => 'FL', + 'country' => 'US', + 'postal_code' => '22599', + 'created_at' => '2025-09-30T04:23:30.823Z', + ]; + + $this->mockResponse($mockedRtpAccount); + + $input = new CreateRtpInput( + receiverId: 're_000000000000', + name: 'John Doe RTP', + beneficiaryName: 'John Doe', + routingNumber: '121000358', + accountNumber: '325203027578', + addressLine1: 'Street of the fools', + addressLine2: null, + city: 'Fools City', + stateProvinceRegion: 'FL', + country: Country::US, + postalCode: '22599' + ); + + $response = $this->blindpay->receivers->bankAccounts->createRtp($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_JW5ZtlKMlgS1', $response->data->id); + $this->assertEquals('rtp', $response->data->type); + $this->assertEquals('John Doe RTP', $response->data->name); + $this->assertEquals('John Doe', $response->data->beneficiaryName); + $this->assertEquals('121000358', $response->data->routingNumber); + $this->assertEquals('325203027578', $response->data->accountNumber); + $this->assertEquals('Street of the fools', $response->data->addressLine1); + $this->assertNull($response->data->addressLine2); + $this->assertEquals('Fools City', $response->data->city); + $this->assertEquals('FL', $response->data->stateProvinceRegion); + $this->assertEquals(Country::US, $response->data->country); + $this->assertEquals('22599', $response->data->postalCode); + } + + #[Test] + public function it_gets_a_bank_account(): void + { + $mockedBankAccount = [ + 'id' => 'ba_000000000000', + 'receiver_id' => 'rcv_123', + 'account_holder_name' => 'Individual full name or business name', + 'account_number' => '1001001234', + 'routing_number' => '012345678', + 'account_type' => 'checking', + 'bank_name' => 'Bank Name', + 'swift_code' => '123456789', + 'iban' => null, + 'is_primary' => false, + 'created_at' => '2021-01-01T00:00:00Z', + 'updated_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedBankAccount); + + $input = new GetBankAccountInput( + receiverId: 're_000000000000', + id: 'ba_000000000000' + ); + + $response = $this->blindpay->receivers->bankAccounts->get($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ba_000000000000', $response->data->id); + $this->assertEquals('rcv_123', $response->data->receiverId); + $this->assertEquals('Individual full name or business name', $response->data->accountHolderName); + $this->assertEquals('1001001234', $response->data->accountNumber); + $this->assertEquals('012345678', $response->data->routingNumber); + $this->assertEquals(BankAccountType::CHECKING, $response->data->accountType); + $this->assertEquals('Bank Name', $response->data->bankName); + $this->assertEquals('123456789', $response->data->swiftCode); + $this->assertNull($response->data->iban); + $this->assertFalse($response->data->isPrimary); + } + + #[Test] + public function it_lists_bank_accounts(): void + { + $mockedBankAccounts = [ + 'data' => [ + [ + 'id' => 'ba_000000000000', + 'type' => 'wire', + 'name' => 'Bank Account Name', + 'pix_key' => '14947677768', + 'beneficiary_name' => 'Individual full name or business name', + 'routing_number' => '012345678', + 'account_number' => '1001001234', + 'account_type' => 'checking', + 'account_class' => 'individual', + 'address_line_1' => 'Address line 1', + 'address_line_2' => 'Address line 2', + 'city' => 'City', + 'state_province_region' => 'State/Province/Region', + 'country' => 'US', + 'postal_code' => 'Postal code', + 'spei_protocol' => 'SPEI', + 'spei_institution_code' => '40002', + 'spei_clabe' => '5482347403740546', + 'transfers_type' => 'CVU', + 'transfers_account' => 'BM123123123123', + 'ach_cop_beneficiary_first_name' => 'Fernando', + 'ach_cop_beneficiary_last_name' => 'Guzman Alarcón', + 'ach_cop_document_id' => '1661105408', + 'ach_cop_document_type' => 'CC', + 'ach_cop_email' => 'fernando.guzman@gmail.com', + 'ach_cop_bank_code' => '051', + 'ach_cop_bank_account' => '12345678', + 'swift_code_bic' => '123456789', + 'swift_account_holder_name' => 'John Doe', + 'swift_account_number_iban' => '123456789', + 'swift_beneficiary_address_line_1' => '123 Main Street, Suite 100, Downtown District, City Center CP 12345', + 'swift_beneficiary_address_line_2' => '456 Oak Avenue, Building 7, Financial District, Business Center CP 54321', + 'swift_beneficiary_country' => 'MX', + 'swift_beneficiary_city' => 'City', + 'swift_beneficiary_state_province_region' => 'District', + 'swift_beneficiary_postal_code' => '11530', + 'swift_bank_name' => 'Banco Regional SA', + 'swift_bank_address_line_1' => '123 Main Street, Suite 100, Downtown District, City Center CP 12345', + 'swift_bank_address_line_2' => '456 Oak Avenue, Building 7, Financial District, Business Center CP 54321', + 'swift_bank_country' => 'MX', + 'swift_bank_city' => 'City', + 'swift_bank_state_province_region' => 'District', + 'swift_bank_postal_code' => '11530', + 'swift_intermediary_bank_swift_code_bic' => 'AEIBARB1', + 'swift_intermediary_bank_account_number_iban' => '123456789', + 'swift_intermediary_bank_name' => 'Banco Regional SA', + 'swift_intermediary_bank_country' => 'US', + 'tron_wallet_hash' => 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', + 'offramp_wallets' => [ + [ + 'address' => 'TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb', + 'id' => 'ow_000000000000', + 'network' => 'tron', + 'external_id' => 'your_external_id', + ], + ], + 'created_at' => '2021-01-01T00:00:00Z', + ], + ], + ]; + + $this->mockResponse($mockedBankAccounts); + + $response = $this->blindpay->receivers->bankAccounts->list('re_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data->data); + $this->assertCount(1, $response->data->data); + $this->assertEquals('ba_000000000000', $response->data->data[0]->id); + $this->assertEquals('Bank Account Name', $response->data->data[0]->name); + $this->assertEquals('14947677768', $response->data->data[0]->pixKey); + $this->assertEquals('Individual full name or business name', $response->data->data[0]->beneficiaryName); + $this->assertEquals('012345678', $response->data->data[0]->routingNumber); + $this->assertEquals('1001001234', $response->data->data[0]->accountNumber); + $this->assertEquals('Address line 1', $response->data->data[0]->addressLine1); + $this->assertEquals('Address line 2', $response->data->data[0]->addressLine2); + $this->assertEquals('City', $response->data->data[0]->city); + } + + #[Test] + public function it_deletes_a_bank_account(): void + { + $this->mockResponse(['data' => null]); + + $input = new DeleteBankAccountInput( + receiverId: 're_000000000000', + id: 'ba_000000000000' + ); + + $response = $this->blindpay->receivers->bankAccounts->delete($input); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } +} diff --git a/tests/Resources/Instances/InstancesTest.php b/tests/Resources/Instances/InstancesTest.php new file mode 100644 index 0000000..1b78b29 --- /dev/null +++ b/tests/Resources/Instances/InstancesTest.php @@ -0,0 +1,151 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_gets_instance_members(): void + { + $mockedMembers = [ + [ + 'id' => 'us_000000000000', + 'email' => 'email@example.com', + 'first_name' => 'Harry', + 'middle_name' => 'James', + 'last_name' => 'Potter', + 'image_url' => 'https://example.com/image.png', + 'created_at' => '2021-01-01T00:00:00Z', + 'role' => 'admin', + ], + ]; + + $this->mockResponse($mockedMembers); + + $response = $this->blindpay->instances->getMembers(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('us_000000000000', $response->data[0]->id); + $this->assertEquals('email@example.com', $response->data[0]->email); + $this->assertEquals('Harry', $response->data[0]->firstName); + $this->assertEquals('James', $response->data[0]->middleName); + $this->assertEquals('Potter', $response->data[0]->lastName); + $this->assertEquals('https://example.com/image.png', $response->data[0]->imageUrl); + $this->assertEquals(InstanceMemberRole::ADMIN, $response->data[0]->role); + } + + #[Test] + public function it_updates_an_instance(): void + { + $this->mockResponse(['data' => null]); + + $input = new UpdateInstanceInput( + name: 'New Instance Name' + ); + + $response = $this->blindpay->instances->update($input); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } + + #[Test] + public function it_deletes_an_instance(): void + { + $this->mockResponse(['data' => null]); + + $response = $this->blindpay->instances->delete(); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } + + #[Test] + public function it_deletes_an_instance_member(): void + { + $this->mockResponse(['data' => null]); + + $response = $this->blindpay->instances->deleteMember('us_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } + + #[Test] + public function it_updates_instance_member_role(): void + { + $this->mockResponse(['data' => null]); + + $input = new UpdateMemberRoleInput( + memberId: 'us_000000000000', + role: InstanceMemberRole::CHECKER + ); + + $response = $this->blindpay->instances->updateMemberRole($input); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } +} diff --git a/tests/Resources/PartnerFees/PartnerFeesTest.php b/tests/Resources/PartnerFees/PartnerFeesTest.php new file mode 100644 index 0000000..cc7965d --- /dev/null +++ b/tests/Resources/PartnerFees/PartnerFeesTest.php @@ -0,0 +1,178 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_lists_partner_fees(): void + { + $mockedList = [ + [ + 'id' => 'fe_000000000000', + 'instance_id' => 'in_000000000000', + 'name' => 'Display Name', + 'payout_percentage_fee' => 0, + 'payout_flat_fee' => 0, + 'payin_percentage_fee' => 0, + 'payin_flat_fee' => 0, + 'evm_wallet_address' => '0x1234567890123456789012345678901234567890', + 'stellar_wallet_address' => 'GAB22222222222222222222222222222222222222222222222222222222222222', + ], + ]; + + $this->mockResponse($mockedList); + + $response = $this->blindpay->partnerFees->list(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('fe_000000000000', $response->data[0]->id); + $this->assertEquals('in_000000000000', $response->data[0]->instanceId); + $this->assertEquals('Display Name', $response->data[0]->name); + $this->assertEquals(0.0, $response->data[0]->payoutPercentageFee); + $this->assertEquals(0.0, $response->data[0]->payoutFlatFee); + $this->assertEquals(0.0, $response->data[0]->payinPercentageFee); + $this->assertEquals(0.0, $response->data[0]->payinFlatFee); + $this->assertEquals('0x1234567890123456789012345678901234567890', $response->data[0]->evmWalletAddress); + $this->assertEquals('GAB22222222222222222222222222222222222222222222222222222222222222', $response->data[0]->stellarWalletAddress); + } + + #[Test] + public function it_creates_a_partner_fee(): void + { + $mockPartnerFee = [ + 'id' => 'fe_000000000000', + 'instance_id' => 'in_000000000000', + 'name' => 'Display Name', + 'payout_percentage_fee' => 0, + 'payout_flat_fee' => 0, + 'payin_percentage_fee' => 0, + 'payin_flat_fee' => 0, + 'evm_wallet_address' => '0x1234567890123456789012345678901234567890', + 'stellar_wallet_address' => 'GAB22222222222222222222222222222222222222222222222222222222222222', + ]; + + $this->mockResponse($mockPartnerFee); + + $input = new CreatePartnerFeeInput( + evmWalletAddress: '0x1234567890123456789012345678901234567890', + name: 'Display Name', + payinFlatFee: 0, + payinPercentageFee: 0, + payoutFlatFee: 0, + payoutPercentageFee: 0, + stellarWalletAddress: 'GAB22222222222222222222222222222222222222222222222222222222222222' + ); + + $response = $this->blindpay->partnerFees->create($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('fe_000000000000', $response->data->id); + $this->assertEquals('in_000000000000', $response->data->instanceId); + $this->assertEquals('Display Name', $response->data->name); + $this->assertEquals(0.0, $response->data->payoutPercentageFee); + $this->assertEquals(0.0, $response->data->payoutFlatFee); + $this->assertEquals(0.0, $response->data->payinPercentageFee); + $this->assertEquals(0.0, $response->data->payinFlatFee); + $this->assertEquals('0x1234567890123456789012345678901234567890', $response->data->evmWalletAddress); + $this->assertEquals('GAB22222222222222222222222222222222222222222222222222222222222222', $response->data->stellarWalletAddress); + } + + #[Test] + public function it_gets_a_partner_fee(): void + { + $mockedFee = [ + 'id' => 'fe_000000000000', + 'instance_id' => 'in_000000000000', + 'name' => 'Display Name', + 'payout_percentage_fee' => 0, + 'payout_flat_fee' => 0, + 'payin_percentage_fee' => 0, + 'payin_flat_fee' => 0, + 'evm_wallet_address' => '0x1234567890123456789012345678901234567890', + 'stellar_wallet_address' => 'GAB22222222222222222222222222222222222222222222222222222222222222', + ]; + + $this->mockResponse($mockedFee); + + $response = $this->blindpay->partnerFees->get('fe_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('fe_000000000000', $response->data->id); + $this->assertEquals('in_000000000000', $response->data->instanceId); + $this->assertEquals('Display Name', $response->data->name); + $this->assertEquals(0.0, $response->data->payoutPercentageFee); + $this->assertEquals(0.0, $response->data->payoutFlatFee); + $this->assertEquals(0.0, $response->data->payinPercentageFee); + $this->assertEquals(0.0, $response->data->payinFlatFee); + $this->assertEquals('0x1234567890123456789012345678901234567890', $response->data->evmWalletAddress); + $this->assertEquals('GAB22222222222222222222222222222222222222222222222222222222222222', $response->data->stellarWalletAddress); + } + + #[Test] + public function it_deletes_a_partner_fee(): void + { + $this->mockResponse(['data' => null]); + + $response = $this->blindpay->partnerFees->delete('fe_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } +} diff --git a/tests/Resources/Payins/PayinQuotesTest.php b/tests/Resources/Payins/PayinQuotesTest.php new file mode 100644 index 0000000..351d417 --- /dev/null +++ b/tests/Resources/Payins/PayinQuotesTest.php @@ -0,0 +1,137 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_creates_a_payin_quote(): void + { + $mockedPayinQuote = [ + 'id' => 'qu_000000000000', + 'expires_at' => 1712958191, + 'commercial_quotation' => 495, + 'blindpay_quotation' => 505, + 'receiver_amount' => 1010, + 'sender_amount' => 5240, + 'partner_fee_amount' => 150, + 'flat_fee' => 50, + ]; + + $this->mockResponse($mockedPayinQuote); + + $payerRules = new PayerRules( + pixAllowedTaxIds: ['149.476.037-68'] + ); + + $input = new CreatePayinQuoteInput( + blockchainWalletId: 'bw_000000000000', + currencyType: CurrencyType::SENDER, + paymentMethod: PaymentMethod::PIX, + requestAmount: 1000, + token: StablecoinToken::USDC, + coverFees: true, + payerRules: $payerRules, + partnerFeeId: 'pf_000000000000' + ); + + $response = $this->blindpay->payins->quotes->create($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('qu_000000000000', $response->data->id); + $this->assertEquals(1712958191, $response->data->expiresAt); + $this->assertEquals(495.0, $response->data->commercialQuotation); + $this->assertEquals(505.0, $response->data->blindpayQuotation); + $this->assertEquals(1010.0, $response->data->receiverAmount); + $this->assertEquals(5240.0, $response->data->senderAmount); + $this->assertEquals(150.0, $response->data->partnerFeeAmount); + $this->assertEquals(50.0, $response->data->flatFee); + } + + #[Test] + public function it_gets_fx_rate(): void + { + $mockedFxRate = [ + 'commercial_quotation' => 495, + 'blindpay_quotation' => 505, + 'result_amount' => 1, + 'instance_flat_fee' => 50, + 'instance_percentage_fee' => 0, + ]; + + $this->mockResponse($mockedFxRate); + + $input = new GetPayinFxRateInput( + currencyType: CurrencyType::SENDER, + from: Currency::USD, + to: Currency::BRL, + requestAmount: 1000 + ); + + $response = $this->blindpay->payins->quotes->getFxRate($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals(495.0, $response->data->commercialQuotation); + $this->assertEquals(505.0, $response->data->blindpayQuotation); + $this->assertEquals(1.0, $response->data->resultAmount); + $this->assertEquals(50.0, $response->data->instanceFlatFee); + $this->assertEquals(0.0, $response->data->instancePercentageFee); + } +} diff --git a/tests/Resources/Payins/PayinsTest.php b/tests/Resources/Payins/PayinsTest.php new file mode 100644 index 0000000..dd64cab --- /dev/null +++ b/tests/Resources/Payins/PayinsTest.php @@ -0,0 +1,331 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + private function getPayinMockData(): array + { + return [ + 'receiver_id' => 're_000000000000', + 'id' => 're_000000000000', + 'pix_code' => '00020101021226790014br.gov.bcb.pix2557brcode.starkinfra.com/v2/bcf07f6c4110454e9fd6f120bab13e835204000053039865802BR5915Blind Pay, Inc.6010Vila Velha62070503***6304BCAB', + 'memo_code' => '8K45GHBNT6BQ6462', + 'clabe' => '014027000000000008', + 'status' => 'processing', + 'payin_quote_id' => 'pq_000000000000', + 'instance_id' => 'in_000000000000', + 'tracking_transaction' => [ + 'step' => 'processing', + 'status' => 'failed', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_payment' => [ + 'step' => 'on_hold', + 'provider_name' => 'blockchain', + 'provider_transaction_id' => 'tx_123456789', + 'provider_status' => 'confirmed', + 'estimated_time_of_arrival' => '2011-10-05T15:00:00.000Z', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_complete' => [ + 'step' => 'on_hold', + 'status' => 'completed', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_partner_fee' => [ + 'step' => 'on_hold', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'created_at' => '2021-01-01T00:00:00Z', + 'updated_at' => '2021-01-01T00:00:00Z', + 'image_url' => 'https://example.com/image.png', + 'first_name' => 'John', + 'last_name' => 'Doe', + 'legal_name' => 'Company Name Inc.', + 'type' => 'individual', + 'payment_method' => 'pix', + 'sender_amount' => 5240, + 'receiver_amount' => 1010, + 'token' => 'USDC', + 'partner_fee_amount' => 150, + 'total_fee_amount' => 1.53, + 'commercial_quotation' => 495, + 'blindpay_quotation' => 505, + 'currency' => 'BRL', + 'billing_fee' => 100, + 'name' => 'Wallet Display Name', + 'address' => '0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', + 'network' => 'polygon', + 'blindpay_bank_details' => [ + 'routing_number' => '121145349', + 'account_number' => '621327727210181', + 'account_type' => 'Business checking', + 'swift_bic_code' => 'CHASUS33', + 'ach' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'wire' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'rtp' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'beneficiary' => [ + 'name' => 'BlindPay, Inc.', + 'address_line_1' => '8 The Green, #19364', + 'address_line_2' => 'Dover, DE 19901', + ], + 'receiving_bank' => [ + 'name' => 'Column NA - Brex', + 'address_line_1' => '1 Letterman Drive, Building A, Suite A4-700', + 'address_line_2' => 'San Francisco, CA 94129', + ], + ], + ]; + } + + #[Test] + public function it_lists_payins(): void + { + $mockedPayins = [ + 'data' => [$this->getPayinMockData()], + 'pagination' => [ + 'has_more' => true, + 'next_page' => 3, + 'prev_page' => 1, + ], + ]; + + $this->mockResponse($mockedPayins); + + $response = $this->blindpay->payins->list(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data->data); + $this->assertCount(1, $response->data->data); + $this->assertEquals('re_000000000000', $response->data->data[0]->id); + $this->assertEquals('re_000000000000', $response->data->data[0]->receiverId); + $this->assertTrue($response->data->pagination->hasMore); + $this->assertEquals(3, $response->data->pagination->nextPage); + $this->assertEquals(1, $response->data->pagination->prevPage); + } + + #[Test] + public function it_gets_a_payin(): void + { + $mockedPayin = $this->getPayinMockData(); + + $this->mockResponse($mockedPayin); + + $response = $this->blindpay->payins->get('pi_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('re_000000000000', $response->data->id); + $this->assertEquals('re_000000000000', $response->data->receiverId); + $this->assertEquals('pq_000000000000', $response->data->payinQuoteId); + $this->assertEquals(TransactionStatus::PROCESSING, $response->data->status); + $this->assertEquals('processing', $response->data->trackingTransaction->step); + $this->assertEquals(StablecoinToken::USDC, $response->data->token); + $this->assertEquals(Network::POLYGON, $response->data->network); + } + + #[Test] + public function it_gets_payin_tracking_information(): void + { + $mockedPayinTrack = $this->getPayinMockData(); + $mockedPayinTrack['tracking_transaction'] = [ + 'step' => 'processing', + 'status' => 'failed', + 'transaction_hash' => '0x123...890', + 'external_id' => '12345678', + 'completed_at' => '2011-10-05T14:48:00.000Z', + 'sender_name' => 'John Doe Smith', + 'sender_tax_id' => '123.456.789-10', + 'sender_bank_code' => '00416968', + 'sender_account_number' => '1234567890', + 'trace_number' => '1234567890', + 'transaction_reference' => '1234567890', + 'description' => 'Payment from John Doe Smith', + ]; + $mockedPayinTrack['tracking_payment'] = [ + 'step' => 'on_hold', + 'provider_name' => 'blockchain', + 'provider_transaction_id' => 'tx_123456789', + 'provider_status' => 'confirmed', + 'estimated_time_of_arrival' => '2011-10-05T15:00:00.000Z', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ]; + + $this->mockResponse($mockedPayinTrack); + + $response = $this->blindpay->payins->getTrack('pi_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('re_000000000000', $response->data->id); + $this->assertEquals('processing', $response->data->trackingTransaction->step); + $this->assertEquals('0x123...890', $response->data->trackingTransaction->transactionHash); + $this->assertEquals('on_hold', $response->data->trackingPayment->step); + $this->assertEquals('blockchain', $response->data->trackingPayment->providerName); + } + + #[Test] + public function it_exports_payins(): void + { + $mockedExportPayins = [$this->getPayinMockData()]; + + $this->mockResponse($mockedExportPayins); + + $input = new ExportPayinsInput( + status: TransactionStatus::PROCESSING + ); + + $response = $this->blindpay->payins->export($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('re_000000000000', $response->data[0]->id); + $this->assertEquals(TransactionStatus::PROCESSING, $response->data[0]->status); + } + + #[Test] + public function it_creates_an_evm_payin(): void + { + $mockedEvmPayin = [ + 'id' => 'pi_000000000000', + 'status' => 'processing', + 'pix_code' => '00020101021226790014br.gov.bcb.pix2557brcode.starkinfra.com/v2/bcf07f6c4110454e9fd6f120bab13e835204000053039865802BR5915Blind Pay, Inc.6010Vila Velha62070503***6304BCAB', + 'memo_code' => '8K45GHBNT6BQ6462', + 'clabe' => '014027000000000008', + 'tracking_complete' => [ + 'step' => 'on_hold', + 'status' => 'completed', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_payment' => [ + 'step' => 'on_hold', + 'provider_name' => 'blockchain', + 'provider_transaction_id' => 'tx_123456789', + 'provider_status' => 'confirmed', + 'estimated_time_of_arrival' => '2011-10-05T15:00:00.000Z', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_transaction' => [ + 'step' => 'processing', + 'status' => 'failed', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_partner_fee' => [ + 'step' => 'on_hold', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'blindpay_bank_details' => [ + 'routing_number' => '121145349', + 'account_number' => '621327727210181', + 'account_type' => 'Business checking', + 'swift_bic_code' => 'CHASUS33', + 'ach' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'wire' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'rtp' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'beneficiary' => [ + 'name' => 'BlindPay, Inc.', + 'address_line_1' => '8 The Green, #19364', + 'address_line_2' => 'Dover, DE 19901', + ], + 'receiving_bank' => [ + 'name' => 'Column NA - Brex', + 'address_line_1' => '1 Letterman Drive, Building A, Suite A4-700', + 'address_line_2' => 'San Francisco, CA 94129', + ], + ], + 'receiver_id' => 're_000000000000', + 'receiver_amount' => 1010, + ]; + + $this->mockResponse($mockedEvmPayin); + + $response = $this->blindpay->payins->createEvm('pq_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('pi_000000000000', $response->data->id); + $this->assertEquals(TransactionStatus::PROCESSING, $response->data->status); + $this->assertEquals('re_000000000000', $response->data->receiverId); + $this->assertEquals(1010.0, $response->data->receiverAmount); + } +} diff --git a/tests/Resources/Payouts/PayoutsTest.php b/tests/Resources/Payouts/PayoutsTest.php new file mode 100644 index 0000000..fd1a5ef --- /dev/null +++ b/tests/Resources/Payouts/PayoutsTest.php @@ -0,0 +1,409 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + private function getPayoutMockData(): array + { + return [ + 'receiver_id' => 're_000000000000', + 'id' => 'pa_000000000000', + 'status' => 'processing', + 'sender_wallet_address' => '0x123...890', + 'signed_transaction' => 'AAA...Zey8y0A', + 'quote_id' => 'qu_000000000000', + 'instance_id' => 'in_000000000000', + 'tracking_transaction' => [ + 'step' => 'processing', + 'status' => 'failed', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_payment' => [ + 'step' => 'on_hold', + 'provider_name' => 'blockchain', + 'provider_transaction_id' => '0x123...890', + 'provider_status' => 'canceled', + 'estimated_time_of_arrival' => '5_min', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_liquidity' => [ + 'step' => 'processing', + 'provider_transaction_id' => '0x123...890', + 'provider_status' => 'deposited', + 'estimated_time_of_arrival' => '1_business_day', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_complete' => [ + 'step' => 'on_hold', + 'status' => 'tokens_refunded', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_partner_fee' => [ + 'step' => 'on_hold', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'created_at' => '2021-01-01T00:00:00Z', + 'updated_at' => '2021-01-01T00:00:00Z', + 'image_url' => 'https://example.com/image.png', + 'first_name' => 'John', + 'last_name' => 'Doe', + 'legal_name' => 'Company Name Inc.', + 'network' => 'sepolia', + 'token' => 'USDC', + 'description' => 'Memo code or description, only works with USD and BRL', + 'sender_amount' => 1010, + 'receiver_amount' => 5240, + 'partner_fee_amount' => 150, + 'commercial_quotation' => 495, + 'blindpay_quotation' => 485, + 'total_fee_amount' => 1.5, + 'receiver_local_amount' => 1000, + 'currency' => 'BRL', + 'transaction_document_file' => 'https://example.com/image.png', + 'transaction_document_type' => 'invoice', + 'transaction_document_id' => '1234567890', + 'name' => 'Bank Account Name', + 'type' => 'wire', + 'pix_key' => '14947677768', + 'account_number' => '1001001234', + 'routing_number' => '012345678', + 'country' => 'US', + 'account_class' => 'individual', + 'address_line_1' => 'Address line 1', + 'address_line_2' => 'Address line 2', + 'city' => 'City', + 'state_province_region' => 'State/Province/Region', + 'postal_code' => 'Postal code', + 'account_type' => 'checking', + 'ach_cop_beneficiary_first_name' => 'Fernando', + 'ach_cop_bank_account' => '12345678', + 'ach_cop_bank_code' => '051', + 'ach_cop_beneficiary_last_name' => 'Guzman Alarcón', + 'ach_cop_document_id' => '1661105408', + 'ach_cop_document_type' => 'CC', + 'ach_cop_email' => 'fernando.guzman@gmail.com', + 'beneficiary_name' => 'Individual full name or business name', + 'spei_clabe' => '5482347403740546', + 'spei_protocol' => 'clabe', + 'spei_institution_code' => '40002', + 'swift_beneficiary_country' => 'MX', + 'swift_code_bic' => '123456789', + 'swift_account_holder_name' => 'John Doe', + 'swift_account_number_iban' => '123456789', + 'transfers_account' => 'BM123123123123', + 'transfers_type' => 'CVU', + 'has_virtual_account' => true, + ]; + } + + #[Test] + public function it_lists_payouts(): void + { + $mockedPayouts = [ + 'data' => [ + $this->getPayoutMockData(), + ], + 'pagination' => [ + 'has_more' => true, + 'next_page' => 3, + 'prev_page' => 1, + ], + ]; + + $this->mockResponse($mockedPayouts); + + $response = $this->blindpay->payouts->list(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data->data); + $this->assertCount(1, $response->data->data); + $this->assertEquals('pa_000000000000', $response->data->data[0]->id); + $this->assertEquals('re_000000000000', $response->data->data[0]->receiverId); + $this->assertEquals('0x123...890', $response->data->data[0]->senderWalletAddress); + $this->assertTrue($response->data->pagination->hasMore); + $this->assertEquals(3, $response->data->pagination->nextPage); + $this->assertEquals(1, $response->data->pagination->prevPage); + } + + #[Test] + public function it_gets_a_payout(): void + { + $mockedPayout = $this->getPayoutMockData(); + + $this->mockResponse($mockedPayout); + + $response = $this->blindpay->payouts->get('pa_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('pa_000000000000', $response->data->id); + $this->assertEquals('re_000000000000', $response->data->receiverId); + $this->assertEquals('processing', $response->data->status->value); + $this->assertEquals('0x123...890', $response->data->senderWalletAddress); + $this->assertEquals('AAA...Zey8y0A', $response->data->signedTransaction); + $this->assertEquals('qu_000000000000', $response->data->quoteId); + $this->assertEquals('in_000000000000', $response->data->instanceId); + $this->assertEquals('John', $response->data->firstName); + $this->assertEquals('Doe', $response->data->lastName); + $this->assertEquals('Company Name Inc.', $response->data->legalName); + $this->assertEquals('sepolia', $response->data->network->value); + $this->assertEquals('USDC', $response->data->token->value); + $this->assertEquals(1010.0, $response->data->senderAmount); + $this->assertEquals(5240.0, $response->data->receiverAmount); + $this->assertEquals('BRL', $response->data->currency->value); + $this->assertEquals('Bank Account Name', $response->data->name); + $this->assertEquals('wire', $response->data->type->value); + $this->assertTrue($response->data->hasVirtualAccount); + } + + #[Test] + public function it_exports_payouts(): void + { + $mockedExportPayouts = [ + $this->getPayoutMockData(), + ]; + + $this->mockResponse($mockedExportPayouts); + + $response = $this->blindpay->payouts->export(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('pa_000000000000', $response->data[0]->id); + $this->assertEquals('re_000000000000', $response->data[0]->receiverId); + $this->assertEquals('processing', $response->data[0]->status->value); + $this->assertEquals('0x123...890', $response->data[0]->senderWalletAddress); + } + + #[Test] + public function it_gets_payout_tracking_information(): void + { + $mockedPayoutTrack = $this->getPayoutMockData(); + + $this->mockResponse($mockedPayoutTrack); + + $response = $this->blindpay->payouts->getTrack('pa_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('pa_000000000000', $response->data->id); + $this->assertEquals('re_000000000000', $response->data->receiverId); + $this->assertEquals('processing', $response->data->status->value); + $this->assertEquals('processing', $response->data->trackingTransaction->step); + $this->assertEquals('failed', $response->data->trackingTransaction->status); + $this->assertEquals('on_hold', $response->data->trackingPayment->step); + $this->assertEquals('blockchain', $response->data->trackingPayment->providerName); + $this->assertEquals('processing', $response->data->trackingLiquidity->step); + $this->assertEquals('deposited', $response->data->trackingLiquidity->providerStatus); + $this->assertEquals('on_hold', $response->data->trackingComplete->step); + $this->assertEquals('tokens_refunded', $response->data->trackingComplete->status); + $this->assertEquals('on_hold', $response->data->trackingPartnerFee->step); + } + + #[Test] + public function it_authorizes_stellar_token(): void + { + $mockedAuthorizeToken = [ + 'transaction_hash' => 'string', + ]; + + $this->mockResponse($mockedAuthorizeToken); + + $input = new AuthorizeStellarTokenInput( + quoteId: 'qu_000000000000', + senderWalletAddress: '0x123...890' + ); + + $response = $this->blindpay->payouts->authorizeStellarToken($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('string', $response->data->transactionHash); + } + + #[Test] + public function it_creates_a_stellar_payout(): void + { + $mockedStellarPayout = [ + 'id' => 'pa_000000000000', + 'status' => 'processing', + 'sender_wallet_address' => '0x123...890', + 'tracking_complete' => [ + 'step' => 'on_hold', + 'status' => 'tokens_refunded', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_payment' => [ + 'step' => 'on_hold', + 'provider_name' => 'blockchain', + 'provider_transaction_id' => '0x123...890', + 'provider_status' => 'canceled', + 'estimated_time_of_arrival' => '5_min', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_transaction' => [ + 'step' => 'processing', + 'status' => 'failed', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_partner_fee' => [ + 'step' => 'on_hold', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_liquidity' => [ + 'step' => 'processing', + 'provider_transaction_id' => '0x123...890', + 'provider_status' => 'deposited', + 'estimated_time_of_arrival' => '1_business_day', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'receiver_id' => 're_000000000000', + ]; + + $this->mockResponse($mockedStellarPayout); + + $input = new CreateStellarPayoutInput( + quoteId: 'qu_000000000000', + senderWalletAddress: '0x123...890' + ); + + $response = $this->blindpay->payouts->createStellar($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('pa_000000000000', $response->data->id); + $this->assertEquals('processing', $response->data->status->value); + $this->assertEquals('0x123...890', $response->data->senderWalletAddress); + $this->assertEquals('re_000000000000', $response->data->receiverId); + $this->assertNotNull($response->data->trackingComplete); + $this->assertNotNull($response->data->trackingPayment); + $this->assertNotNull($response->data->trackingTransaction); + $this->assertNotNull($response->data->trackingPartnerFee); + $this->assertNotNull($response->data->trackingLiquidity); + } + + #[Test] + public function it_creates_an_evm_payout(): void + { + $mockedEvmPayout = [ + 'id' => 'pa_000000000000', + 'status' => 'processing', + 'sender_wallet_address' => '0x123...890', + 'tracking_complete' => [ + 'step' => 'on_hold', + 'status' => 'tokens_refunded', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_payment' => [ + 'step' => 'on_hold', + 'provider_name' => 'blockchain', + 'provider_transaction_id' => '0x123...890', + 'provider_status' => 'canceled', + 'estimated_time_of_arrival' => '5_min', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_transaction' => [ + 'step' => 'processing', + 'status' => 'failed', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_partner_fee' => [ + 'step' => 'on_hold', + 'transaction_hash' => '0x123...890', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'tracking_liquidity' => [ + 'step' => 'processing', + 'provider_transaction_id' => '0x123...890', + 'provider_status' => 'deposited', + 'estimated_time_of_arrival' => '1_business_day', + 'completed_at' => '2011-10-05T14:48:00.000Z', + ], + 'receiver_id' => 're_000000000000', + ]; + + $this->mockResponse($mockedEvmPayout); + + $input = new CreateEvmPayoutInput( + quoteId: 'qu_000000000000', + senderWalletAddress: '0x123...890' + ); + + $response = $this->blindpay->payouts->createEvm($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('pa_000000000000', $response->data->id); + $this->assertEquals('processing', $response->data->status->value); + $this->assertEquals('0x123...890', $response->data->senderWalletAddress); + $this->assertEquals('re_000000000000', $response->data->receiverId); + $this->assertNotNull($response->data->trackingComplete); + $this->assertNotNull($response->data->trackingPayment); + $this->assertNotNull($response->data->trackingTransaction); + $this->assertNotNull($response->data->trackingPartnerFee); + $this->assertNotNull($response->data->trackingLiquidity); + } +} diff --git a/tests/Resources/Quotes/QuotesTest.php b/tests/Resources/Quotes/QuotesTest.php new file mode 100644 index 0000000..56620c4 --- /dev/null +++ b/tests/Resources/Quotes/QuotesTest.php @@ -0,0 +1,161 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_creates_a_quote(): void + { + $mockedQuote = [ + 'id' => 'qu_000000000000', + 'expires_at' => 1712958191, + 'commercial_quotation' => 495, + 'blindpay_quotation' => 485, + 'receiver_amount' => 5240, + 'sender_amount' => 1010, + 'partner_fee_amount' => 150, + 'flat_fee' => 50, + 'contract' => [ + 'abi' => [[]], + 'address' => '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', + 'functionName' => 'approve', + 'blindpayContractAddress' => '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', + 'amount' => '1000000000000000000', + 'network' => [ + 'name' => 'Ethereum', + 'chainId' => 1, + ], + ], + 'receiver_local_amount' => 1000, + 'description' => 'Memo code or description, only works with USD and BRL', + ]; + + $this->mockResponse($mockedQuote); + + $input = new CreateQuoteInput( + bankAccountId: 'ba_000000000000', + currencyType: CurrencyType::SENDER, + requestAmount: 1000, + coverFees: true, + partnerFeeId: 'pf_000000000000', + transactionDocumentFile: null, + transactionDocumentId: null, + transactionDocumentType: TransactionDocumentType::INVOICE, + network: Network::SEPOLIA, + token: StablecoinToken::USDC, + description: 'Memo code or description, only works with USD and BRL' + ); + + $response = $this->blindpay->quotes->create($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('qu_000000000000', $response->data->id); + $this->assertEquals(1712958191, $response->data->expiresAt); + $this->assertEquals(495.0, $response->data->commercialQuotation); + $this->assertEquals(485.0, $response->data->blindpayQuotation); + $this->assertEquals(5240.0, $response->data->receiverAmount); + $this->assertEquals(1010.0, $response->data->senderAmount); + $this->assertEquals(150.0, $response->data->partnerFeeAmount); + $this->assertEquals(50.0, $response->data->flatFee); + $this->assertEquals(1000.0, $response->data->receiverLocalAmount); + $this->assertEquals('Memo code or description, only works with USD and BRL', $response->data->description); + + // Assert contract details + $this->assertIsArray($response->data->contract->abi); + $this->assertCount(1, $response->data->contract->abi); + $this->assertEquals('0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', $response->data->contract->address); + $this->assertEquals('approve', $response->data->contract->functionName); + $this->assertEquals('0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', $response->data->contract->blindpayContractAddress); + $this->assertEquals('1000000000000000000', $response->data->contract->amount); + $this->assertEquals('Ethereum', $response->data->contract->network->name); + $this->assertEquals(1, $response->data->contract->network->chainId); + } + + #[Test] + public function it_gets_fx_rate(): void + { + $mockedFxRate = [ + 'commercial_quotation' => 495, + 'blindpay_quotation' => 485, + 'result_amount' => 1, + 'instance_flat_fee' => 50, + 'instance_percentage_fee' => 0, + ]; + + $this->mockResponse($mockedFxRate); + + $input = new GetFxRateInput( + currencyType: CurrencyType::SENDER, + from: Currency::USD, + to: Currency::BRL, + requestAmount: 1000 + ); + + $response = $this->blindpay->quotes->getFxRate($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals(495.0, $response->data->commercialQuotation); + $this->assertEquals(485.0, $response->data->blindpayQuotation); + $this->assertEquals(1.0, $response->data->resultAmount); + $this->assertEquals(50.0, $response->data->instanceFlatFee); + $this->assertEquals(0.0, $response->data->instancePercentageFee); + } +} diff --git a/tests/Resources/Receivers/ReceiversTest.php b/tests/Resources/Receivers/ReceiversTest.php new file mode 100644 index 0000000..4f9f848 --- /dev/null +++ b/tests/Resources/Receivers/ReceiversTest.php @@ -0,0 +1,618 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_lists_receivers(): void + { + $mockedReceivers = [ + [ + 'id' => 're_Euw7HN4OdxPn', + 'type' => 'individual', + 'kyc_type' => 'standard', + 'kyc_status' => 'verifying', + 'kyc_warnings' => [ + [ + 'code' => null, + 'message' => null, + 'resolution_status' => null, + 'warning_id' => null, + ], + ], + 'email' => 'bernardo@gmail.com', + 'tax_id' => '12345678900', + 'address_line_1' => 'Av. Paulista, 1000', + 'address_line_2' => 'Apto 101', + 'city' => 'São Paulo', + 'state_province_region' => 'SP', + 'country' => 'BR', + 'postal_code' => '01310-100', + 'ip_address' => '127.0.0.1', + 'image_url' => 'https://example.com/image.png', + 'phone_number' => '+5511987654321', + 'proof_of_address_doc_type' => 'UTILITY_BILL', + 'proof_of_address_doc_file' => 'https://example.com/image.png', + 'first_name' => 'Bernardo', + 'last_name' => 'Simonassi', + 'date_of_birth' => '1998-02-02T00:00:00.000Z', + 'id_doc_country' => 'BR', + 'id_doc_type' => 'PASSPORT', + 'id_doc_front_file' => 'https://example.com/image.png', + 'id_doc_back_file' => 'https://example.com/image.png', + 'aiprise_validation_key' => '', + 'instance_id' => 'in_000000000000', + 'tos_id' => 'to_3ZZhllJkvo5Z', + 'created_at' => '2021-01-01T00:00:00.000Z', + 'updated_at' => '2021-01-01T00:00:00.000Z', + 'limit' => [ + 'per_transaction' => 100000, + 'daily' => 200000, + 'monthly' => 1000000, + ], + ], + [ + 'id' => 're_YuaMcI2B8zbQ', + 'type' => 'individual', + 'kyc_type' => 'enhanced', + 'kyc_status' => 'approved', + 'kyc_warnings' => null, + 'email' => 'alice.johnson@example.com', + 'tax_id' => '98765432100', + 'address_line_1' => '123 Main St', + 'address_line_2' => null, + 'city' => 'New York', + 'state_province_region' => 'NY', + 'country' => 'US', + 'postal_code' => '10001', + 'ip_address' => '192.168.1.1', + 'image_url' => null, + 'phone_number' => '+15555555555', + 'proof_of_address_doc_type' => 'BANK_STATEMENT', + 'proof_of_address_doc_file' => 'https://example.com/image.png', + 'first_name' => 'Alice', + 'last_name' => 'Johnson', + 'date_of_birth' => '1990-05-10T00:00:00.000Z', + 'id_doc_country' => 'US', + 'id_doc_type' => 'PASSPORT', + 'id_doc_front_file' => 'https://example.com/image.png', + 'id_doc_back_file' => null, + 'aiprise_validation_key' => 'enhanced-key', + 'instance_id' => 'in_000000000001', + 'source_of_funds_doc_type' => 'salary', + 'source_of_funds_doc_file' => 'https://example.com/image.png', + 'individual_holding_doc_front_file' => 'https://example.com/image.png', + 'purpose_of_transactions' => 'investment_purposes', + 'purpose_of_transactions_explanation' => 'Investing in stocks', + 'tos_id' => 'to_nppX66ntvtHs', + 'created_at' => '2022-02-02T00:00:00.000Z', + 'updated_at' => '2022-02-02T00:00:00.000Z', + 'limit' => [ + 'per_transaction' => 50000, + 'daily' => 100000, + 'monthly' => 500000, + ], + ], + [ + 'id' => 're_IOxAUL24LG7P', + 'type' => 'business', + 'kyc_type' => 'standard', + 'kyc_status' => 'pending', + 'kyc_warnings' => null, + 'email' => 'business@example.com', + 'tax_id' => '20096178000195', + 'address_line_1' => '1 King St W', + 'address_line_2' => 'Suite 100', + 'city' => 'Toronto', + 'state_province_region' => 'ON', + 'country' => 'CA', + 'postal_code' => 'M5H 1A1', + 'ip_address' => null, + 'image_url' => null, + 'phone_number' => '+14165555555', + 'proof_of_address_doc_type' => 'UTILITY_BILL', + 'proof_of_address_doc_file' => 'https://example.com/image.png', + 'legal_name' => 'Business Corp', + 'alternate_name' => 'BizCo', + 'formation_date' => '2010-01-01T00:00:00.000Z', + 'website' => 'https://businesscorp.com', + 'owners' => [ + [ + 'role' => 'beneficial_owner', + 'first_name' => 'Carlos', + 'last_name' => 'Silva', + 'date_of_birth' => '1995-05-15T00:00:00.000Z', + 'tax_id' => '12345678901', + 'address_line_1' => 'Rua Augusta, 1500', + 'address_line_2' => null, + 'city' => 'São Paulo', + 'state_province_region' => 'SP', + 'country' => 'BR', + 'postal_code' => '01304-001', + 'id_doc_country' => 'BR', + 'id_doc_type' => 'PASSPORT', + 'id_doc_front_file' => 'https://example.com/image.png', + 'id_doc_back_file' => 'https://example.com/image.png', + 'proof_of_address_doc_type' => 'UTILITY_BILL', + 'proof_of_address_doc_file' => 'https://example.com/image.png', + 'id' => 'ub_000000000000', + 'instance_id' => 'in_000000000000', + 'receiver_id' => 're_IOxAUL24LG7P', + ], + ], + 'incorporation_doc_file' => 'https://example.com/image.png', + 'proof_of_ownership_doc_file' => 'https://example.com/image.png', + 'external_id' => null, + 'instance_id' => 'in_000000000002', + 'tos_id' => 'to_nppX66ntvtHs', + 'aiprise_validation_key' => '', + 'created_at' => '2015-03-15T00:00:00.000Z', + 'updated_at' => '2015-03-15T00:00:00.000Z', + 'limit' => [ + 'per_transaction' => 200000, + 'daily' => 400000, + 'monthly' => 2000000, + ], + ], + ]; + + $this->mockResponse($mockedReceivers); + + $response = $this->blindpay->receivers->list(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(3, $response->data); + $this->assertEquals('re_Euw7HN4OdxPn', $response->data[0]->id); + $this->assertEquals('re_YuaMcI2B8zbQ', $response->data[1]->id); + $this->assertEquals('re_IOxAUL24LG7P', $response->data[2]->id); + } + + #[Test] + public function it_creates_an_individual_receiver_with_standard_kyc(): void + { + $mockedReceiver = [ + 'id' => 're_Euw7HN4OdxPn', + ]; + + $this->mockResponse($mockedReceiver); + + $input = new CreateIndividualWithStandardKYCInput( + addressLine1: 'Av. Paulista, 1000', + city: 'São Paulo', + country: Country::BR, + dateOfBirth: '1998-02-02T00:00:00.000Z', + email: 'bernardo.simonassi@gmail.com', + firstName: 'Bernardo', + idDocBackFile: 'https://example.com/image.png', + idDocCountry: Country::BR, + idDocFrontFile: 'https://example.com/image.png', + idDocType: IdentificationDocument::PASSPORT, + lastName: 'Simonassi', + phoneNumber: '+5511987654321', + postalCode: '01310-100', + proofOfAddressDocFile: 'https://example.com/image.png', + proofOfAddressDocType: ProofOfAddressDocType::UTILITY_BILL, + stateProvinceRegion: 'SP', + taxId: '12345678900', + tosId: 'to_tPiz4bM2nh5K', + addressLine2: 'Apto 101' + ); + + $response = $this->blindpay->receivers->createIndividualWithStandardKYC($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('re_Euw7HN4OdxPn', $response->data->id); + } + + #[Test] + public function it_creates_an_individual_receiver_with_enhanced_kyc(): void + { + $mockedReceiver = [ + 'id' => 're_YuaMcI2B8zbQ', + ]; + + $this->mockResponse($mockedReceiver); + + $input = new CreateIndividualWithEnhancedKYCInput( + addressLine1: 'Av. Paulista, 1000', + city: 'São Paulo', + country: Country::BR, + dateOfBirth: '1998-02-02T00:00:00.000Z', + email: 'bernardo.simonassi@gmail.com', + firstName: 'Bernardo', + idDocBackFile: 'https://example.com/image.png', + idDocCountry: Country::BR, + idDocFrontFile: 'https://example.com/image.png', + idDocType: IdentificationDocument::PASSPORT, + individualHoldingDocFrontFile: 'https://example.com/image.png', + lastName: 'Simonassi', + phoneNumber: '+5511987654321', + postalCode: '01310-100', + proofOfAddressDocFile: 'https://example.com/image.png', + proofOfAddressDocType: ProofOfAddressDocType::UTILITY_BILL, + purposeOfTransactions: PurposeOfTransactions::PERSONAL_OR_LIVING_EXPENSES, + purposeOfTransactionsExplanation: 'I am receiving salary payments from my employer', + sourceOfFundsDocFile: 'https://example.com/image.png', + sourceOfFundsDocType: SourceOfFundsDocType::SAVINGS, + stateProvinceRegion: 'SP', + taxId: '12345678900', + tosId: 'to_3ZZhllJkvo5Z', + addressLine2: 'Apto 101' + ); + + $response = $this->blindpay->receivers->createIndividualWithEnhancedKYC($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('re_YuaMcI2B8zbQ', $response->data->id); + } + + #[Test] + public function it_creates_a_business_receiver_with_standard_kyb(): void + { + $mockedReceiver = [ + 'id' => 're_IOxAUL24LG7P', + ]; + + $this->mockResponse($mockedReceiver); + + $owner = new class + { + public function toArray(): array + { + return [ + 'role' => 'beneficial_owner', + 'first_name' => 'Carlos', + 'last_name' => 'Silva', + 'date_of_birth' => '1995-05-15T00:00:00.000Z', + 'tax_id' => '12345678901', + 'address_line_1' => 'Rua Augusta, 1500', + 'address_line_2' => null, + 'city' => 'São Paulo', + 'state_province_region' => 'SP', + 'country' => 'BR', + 'postal_code' => '01304-001', + 'id_doc_country' => 'BR', + 'id_doc_type' => 'PASSPORT', + 'id_doc_front_file' => 'https://example.com/image.png', + 'id_doc_back_file' => 'https://example.com/image.png', + 'proof_of_address_doc_type' => 'UTILITY_BILL', + 'proof_of_address_doc_file' => 'https://example.com/image.png', + ]; + } + }; + + $input = new CreateBusinessWithStandardKYBInput( + addressLine1: 'Av. Brigadeiro Faria Lima, 400', + city: 'São Paulo', + country: Country::BR, + email: 'contato@empresa.com.br', + formationDate: '2010-05-20T00:00:00.000Z', + incorporationDocFile: 'https://example.com/image.png', + legalName: 'Empresa Exemplo Ltda', + owners: [$owner], + postalCode: '04538-132', + proofOfAddressDocFile: 'https://example.com/image.png', + proofOfAddressDocType: ProofOfAddressDocType::UTILITY_BILL, + proofOfOwnershipDocFile: 'https://example.com/image.png', + stateProvinceRegion: 'SP', + taxId: '20096178000195', + tosId: 'to_nppX66ntvtHs', + addressLine2: 'Sala 1201', + alternateName: 'Exemplo', + website: 'https://site.com/' + ); + + $response = $this->blindpay->receivers->createBusinessWithStandardKYB($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('re_IOxAUL24LG7P', $response->data->id); + } + + #[Test] + public function it_gets_a_receiver(): void + { + $mockedReceiver = [ + 'id' => 're_YuaMcI2B8zbQ', + 'type' => 'individual', + 'kyc_type' => 'enhanced', + 'kyc_status' => 'verifying', + 'kyc_warnings' => [ + [ + 'code' => null, + 'message' => null, + 'resolution_status' => null, + 'warning_id' => null, + ], + ], + 'email' => 'bernardo.simonassi@gmail.com', + 'tax_id' => '12345678900', + 'address_line_1' => 'Av. Paulista, 1000', + 'address_line_2' => 'Apto 101', + 'city' => 'São Paulo', + 'state_province_region' => 'SP', + 'country' => 'BR', + 'postal_code' => '01310-100', + 'ip_address' => '127.0.0.1', + 'image_url' => 'https://example.com/image.png', + 'phone_number' => '+5511987654321', + 'proof_of_address_doc_type' => 'UTILITY_BILL', + 'proof_of_address_doc_file' => 'https://example.com/image.png', + 'first_name' => 'Bernardo', + 'last_name' => 'Simonassi', + 'date_of_birth' => '1998-02-02T00:00:00.000Z', + 'id_doc_country' => 'BR', + 'id_doc_type' => 'PASSPORT', + 'id_doc_front_file' => 'https://example.com/image.png', + 'id_doc_back_file' => 'https://example.com/image.png', + 'aiprise_validation_key' => '', + 'source_of_funds_doc_type' => 'savings', + 'source_of_funds_doc_file' => 'https://example.com/image.png', + 'individual_holding_doc_front_file' => 'https://example.com/image.png', + 'purpose_of_transactions' => 'personal_or_living_expenses', + 'purpose_of_transactions_explanation' => 'I am receiving salary payments from my employer', + 'instance_id' => 'in_000000000000', + 'tos_id' => 'to_3ZZhllJkvo5Z', + 'created_at' => '2021-01-01T00:00:00.000Z', + 'updated_at' => '2021-01-01T00:00:00.000Z', + 'limit' => [ + 'per_transaction' => 100000, + 'daily' => 200000, + 'monthly' => 1000000, + ], + ]; + + $this->mockResponse($mockedReceiver); + + $response = $this->blindpay->receivers->get('re_YuaMcI2B8zbQ'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('re_YuaMcI2B8zbQ', $response->data->id); + $this->assertEquals('bernardo.simonassi@gmail.com', $response->data->email); + $this->assertEquals('Bernardo', $response->data->firstName); + $this->assertEquals('Simonassi', $response->data->lastName); + } + + #[Test] + public function it_updates_a_receiver(): void + { + $this->mockResponse(['data' => null]); + + $input = new UpdateReceiverInput( + receiverId: 're_YuaMcI2B8zbQ', + email: 'bernardo.simonassi@gmail.com', + taxId: '12345678900', + addressLine1: 'Av. Paulista, 1000', + addressLine2: 'Apto 101', + city: 'São Paulo', + stateProvinceRegion: 'SP', + country: Country::BR, + postalCode: '01310-100', + ipAddress: '127.0.0.1', + imageUrl: 'https://example.com/image.png', + phoneNumber: '+5511987654321', + proofOfAddressDocType: ProofOfAddressDocType::UTILITY_BILL, + proofOfAddressDocFile: 'https://example.com/image.png', + firstName: 'Bernardo', + lastName: 'Simonassi', + dateOfBirth: '1998-02-02T00:00:00.000Z', + idDocCountry: Country::BR, + idDocType: IdentificationDocument::PASSPORT, + idDocFrontFile: 'https://example.com/image.png', + idDocBackFile: 'https://example.com/image.png', + alternateName: 'Exemplo', + formationDate: '2010-05-20T00:00:00.000Z', + website: 'https://site.com', + owners: [[ + 'id' => 'ub_000000000000', + 'first_name' => 'Carlos', + 'last_name' => 'Silva', + 'role' => 'beneficial_owner', + 'date_of_birth' => '1995-05-15T00:00:00.000Z', + 'tax_id' => '12345678901', + 'address_line_1' => 'Rua Augusta, 1500', + 'address_line_2' => null, + 'city' => 'São Paulo', + 'state_province_region' => 'SP', + 'country' => 'BR', + 'postal_code' => '01304-001', + 'id_doc_country' => 'BR', + 'id_doc_type' => 'PASSPORT', + 'id_doc_front_file' => 'https://example.com/image.png', + 'id_doc_back_file' => 'https://example.com/image.png', + ]], + incorporationDocFile: 'https://example.com/image.png', + proofOfOwnershipDocFile: 'https://example.com/image.png', + sourceOfFundsDocType: SourceOfFundsDocType::SAVINGS, + sourceOfFundsDocFile: 'https://example.com/image.png', + individualHoldingDocFrontFile: 'https://example.com/image.png', + purposeOfTransactions: PurposeOfTransactions::PERSONAL_OR_LIVING_EXPENSES, + purposeOfTransactionsExplanation: 'I am receiving salary payments from my employer', + externalId: 'some-external-id', + tosId: 'to_3ZZhllJkvo5Z' + ); + + $response = $this->blindpay->receivers->update($input); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } + + #[Test] + public function it_deletes_a_receiver(): void + { + $this->mockResponse(['data' => null]); + + $response = $this->blindpay->receivers->delete('re_YuaMcI2B8zbQ'); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } + + #[Test] + public function it_gets_receiver_limits(): void + { + $mockedReceiverLimits = [ + 'limits' => [ + 'payin' => [ + 'daily' => 10000, + 'monthly' => 50000, + ], + 'payout' => [ + 'daily' => 20000, + 'monthly' => 100000, + ], + ], + ]; + + $this->mockResponse($mockedReceiverLimits); + + $response = $this->blindpay->receivers->getLimits('re_YuaMcI2B8zbQ'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsObject($response->data); + $this->assertObjectHasProperty('limits', $response->data); + $this->assertEquals(10000.0, $response->data->limits['payin']['daily']); + $this->assertEquals(50000.0, $response->data->limits['payin']['monthly']); + $this->assertEquals(20000.0, $response->data->limits['payout']['daily']); + $this->assertEquals(100000.0, $response->data->limits['payout']['monthly']); + } + + #[Test] + public function it_gets_limit_increase_requests_for_a_receiver(): void + { + $mockedLimitIncreaseRequests = [ + [ + 'id' => 'rl_000000000000', + 'receiver_id' => 're_YuaMcI2B8zbQ', + 'status' => 'in_review', + 'daily' => 50000, + 'monthly' => 250000, + 'per_transaction' => 25000, + 'supporting_document_file' => 'https://example.com/bank-statement.pdf', + 'supporting_document_type' => 'individual_bank_statement', + 'created_at' => '2025-01-15T10:30:00.000Z', + 'updated_at' => '2025-01-15T10:30:00.000Z', + ], + [ + 'id' => 'rl_000000000000', + 'receiver_id' => 're_YuaMcI2B8zbQ', + 'status' => 'approved', + 'daily' => 30000, + 'monthly' => 150000, + 'per_transaction' => 15000, + 'supporting_document_file' => 'https://example.com/proof-of-income.pdf', + 'supporting_document_type' => 'individual_proof_of_income', + 'created_at' => '2024-12-10T14:20:00.000Z', + 'updated_at' => '2024-12-12T09:45:00.000Z', + ], + ]; + + $this->mockResponse($mockedLimitIncreaseRequests); + + $response = $this->blindpay->receivers->getLimitIncreaseRequests('re_YuaMcI2B8zbQ'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(2, $response->data); + $this->assertEquals('rl_000000000000', $response->data[0]->id); + $this->assertEquals('in_review', $response->data[0]->status->value); + $this->assertEquals('approved', $response->data[1]->status->value); + } + + #[Test] + public function it_requests_a_limit_increase_for_a_receiver(): void + { + $mockedResponse = [ + 'id' => 'rl_000000000000', + ]; + + $this->mockResponse($mockedResponse); + + $input = new RequestLimitIncreaseInput( + receiverId: 're_YuaMcI2B8zbQ', + daily: 100000, + monthly: 500000, + perTransaction: 50000, + supportingDocumentFile: 'https://example.com/tax-return.pdf', + supportingDocumentType: LimitIncreaseRequestSupportingDocumentType::INDIVIDUAL_TAX_RETURN + ); + + $response = $this->blindpay->receivers->requestLimitIncrease($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('rl_000000000000', $response->data->id); + } +} diff --git a/tests/Resources/VirtualAccounts/VirtualAccountsTest.php b/tests/Resources/VirtualAccounts/VirtualAccountsTest.php new file mode 100644 index 0000000..79a1539 --- /dev/null +++ b/tests/Resources/VirtualAccounts/VirtualAccountsTest.php @@ -0,0 +1,202 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_updates_a_virtual_account(): void + { + $this->mockResponse(['data' => null]); + + $input = new UpdateVirtualAccountInput( + receiverId: 're_000000000000', + blockchainWalletId: 'bw_000000000000', + token: StablecoinToken::USDC + ); + + $response = $this->blindpay->virtualAccounts->update($input); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } + + #[Test] + public function it_creates_a_virtual_account(): void + { + $mockedVirtualAccount = [ + 'id' => 'va_000000000000', + 'us' => [ + 'ach' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'wire' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'rtp' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'swift_bic_code' => 'CHASUS33', + 'account_type' => 'Business checking', + 'beneficiary' => [ + 'name' => 'Receiver Name', + 'address_line_1' => '8 The Green, #19364', + 'address_line_2' => 'Dover, DE 19901', + ], + 'receiving_bank' => [ + 'name' => 'JPMorgan Chase', + 'address_line_1' => '270 Park Ave', + 'address_line_2' => 'New York, NY, 10017-2070', + ], + ], + 'token' => 'USDC', + 'blockchain_wallet_id' => 'bw_000000000000', + ]; + + $this->mockResponse($mockedVirtualAccount); + + $input = new CreateVirtualAccountInput( + receiverId: 're_000000000000', + blockchainWalletId: 'bw_000000000000', + token: StablecoinToken::USDC + ); + + $response = $this->blindpay->virtualAccounts->create($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('va_000000000000', $response->data->id); + $this->assertEquals(StablecoinToken::USDC, $response->data->token); + $this->assertEquals('bw_000000000000', $response->data->blockchainWalletId); + $this->assertEquals('123456789', $response->data->us->ach->routingNumber); + $this->assertEquals('123456789', $response->data->us->ach->accountNumber); + $this->assertEquals('123456789', $response->data->us->wire->routingNumber); + $this->assertEquals('123456789', $response->data->us->wire->accountNumber); + $this->assertEquals('123456789', $response->data->us->rtp->routingNumber); + $this->assertEquals('123456789', $response->data->us->rtp->accountNumber); + $this->assertEquals('CHASUS33', $response->data->us->swiftBicCode); + $this->assertEquals('Business checking', $response->data->us->accountType); + $this->assertEquals('Receiver Name', $response->data->us->beneficiary->name); + $this->assertEquals('8 The Green, #19364', $response->data->us->beneficiary->addressLine1); + $this->assertEquals('Dover, DE 19901', $response->data->us->beneficiary->addressLine2); + $this->assertEquals('JPMorgan Chase', $response->data->us->receivingBank->name); + $this->assertEquals('270 Park Ave', $response->data->us->receivingBank->addressLine1); + $this->assertEquals('New York, NY, 10017-2070', $response->data->us->receivingBank->addressLine2); + } + + #[Test] + public function it_gets_a_virtual_account(): void + { + $mockedVirtualAccount = [ + 'id' => 'va_000000000000', + 'us' => [ + 'ach' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'wire' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'rtp' => [ + 'routing_number' => '123456789', + 'account_number' => '123456789', + ], + 'swift_bic_code' => 'CHASUS33', + 'account_type' => 'Business checking', + 'beneficiary' => [ + 'name' => 'Receiver Name', + 'address_line_1' => '8 The Green, #19364', + 'address_line_2' => 'Dover, DE 19901', + ], + 'receiving_bank' => [ + 'name' => 'JPMorgan Chase', + 'address_line_1' => '270 Park Ave', + 'address_line_2' => 'New York, NY, 10017-2070', + ], + ], + 'token' => 'USDC', + 'blockchain_wallet_id' => 'bw_000000000000', + ]; + + $this->mockResponse($mockedVirtualAccount); + + $response = $this->blindpay->virtualAccounts->get('re_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('va_000000000000', $response->data->id); + $this->assertEquals(StablecoinToken::USDC, $response->data->token); + $this->assertEquals('bw_000000000000', $response->data->blockchainWalletId); + $this->assertEquals('123456789', $response->data->us->ach->routingNumber); + $this->assertEquals('123456789', $response->data->us->ach->accountNumber); + $this->assertEquals('123456789', $response->data->us->wire->routingNumber); + $this->assertEquals('123456789', $response->data->us->wire->accountNumber); + $this->assertEquals('123456789', $response->data->us->rtp->routingNumber); + $this->assertEquals('123456789', $response->data->us->rtp->accountNumber); + $this->assertEquals('CHASUS33', $response->data->us->swiftBicCode); + $this->assertEquals('Business checking', $response->data->us->accountType); + $this->assertEquals('Receiver Name', $response->data->us->beneficiary->name); + $this->assertEquals('8 The Green, #19364', $response->data->us->beneficiary->addressLine1); + $this->assertEquals('Dover, DE 19901', $response->data->us->beneficiary->addressLine2); + $this->assertEquals('JPMorgan Chase', $response->data->us->receivingBank->name); + $this->assertEquals('270 Park Ave', $response->data->us->receivingBank->addressLine1); + $this->assertEquals('New York, NY, 10017-2070', $response->data->us->receivingBank->addressLine2); + } +} diff --git a/tests/Resources/Wallets/BlockchainWalletsTest.php b/tests/Resources/Wallets/BlockchainWalletsTest.php new file mode 100644 index 0000000..d5bc565 --- /dev/null +++ b/tests/Resources/Wallets/BlockchainWalletsTest.php @@ -0,0 +1,221 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_gets_blockchain_wallet_message(): void + { + $mockedMessage = [ + 'message' => 'random', + ]; + + $this->mockResponse($mockedMessage); + + $response = $this->blindpay->wallets->blockchain->getMessage('re_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('random', $response->data->message); + } + + #[Test] + public function it_lists_blockchain_wallets(): void + { + $mockedWallets = [ + [ + 'id' => 'bw_000000000000', + 'name' => 'Wallet Display Name', + 'network' => 'polygon', + 'address' => '0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', + 'signature_tx_hash' => '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', + 'is_account_abstraction' => false, + 'receiver_id' => 're_000000000000', + ], + ]; + + $this->mockResponse($mockedWallets); + + $response = $this->blindpay->wallets->blockchain->list('re_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('bw_000000000000', $response->data[0]->id); + $this->assertEquals('Wallet Display Name', $response->data[0]->name); + $this->assertEquals(Network::POLYGON, $response->data[0]->network); + $this->assertEquals('0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', $response->data[0]->address); + $this->assertFalse($response->data[0]->isAccountAbstraction); + } + + #[Test] + public function it_creates_a_blockchain_wallet_with_address(): void + { + $mockedWallet = [ + 'id' => 'bw_000000000000', + 'name' => 'Wallet Display Name', + 'network' => 'polygon', + 'address' => '0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', + 'signature_tx_hash' => null, + 'is_account_abstraction' => true, + 'receiver_id' => 're_000000000000', + ]; + + $this->mockResponse($mockedWallet); + + $input = new CreateBlockchainWalletWithAddressInput( + receiverId: 're_000000000000', + name: 'Wallet Display Name', + network: Network::POLYGON, + address: '0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C' + ); + + $response = $this->blindpay->wallets->blockchain->createWithAddress($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('bw_000000000000', $response->data->id); + $this->assertEquals('Wallet Display Name', $response->data->name); + $this->assertEquals(Network::POLYGON, $response->data->network); + $this->assertEquals('0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', $response->data->address); + $this->assertNull($response->data->signatureTxHash); + $this->assertTrue($response->data->isAccountAbstraction); + } + + #[Test] + public function it_creates_a_blockchain_wallet_with_hash(): void + { + $mockedWallet = [ + 'id' => 'bw_000000000000', + 'name' => 'Wallet Display Name', + 'network' => 'polygon', + 'address' => '0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', + 'signature_tx_hash' => '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', + 'is_account_abstraction' => false, + 'receiver_id' => 're_000000000000', + ]; + + $this->mockResponse($mockedWallet); + + $input = new CreateBlockchainWalletWithHashInput( + receiverId: 're_000000000000', + name: 'Wallet Display Name', + network: Network::POLYGON, + signatureTxHash: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359' + ); + + $response = $this->blindpay->wallets->blockchain->createWithHash($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('bw_000000000000', $response->data->id); + $this->assertEquals('Wallet Display Name', $response->data->name); + $this->assertEquals(Network::POLYGON, $response->data->network); + $this->assertEquals('0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', $response->data->address); + $this->assertEquals('0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', $response->data->signatureTxHash); + $this->assertFalse($response->data->isAccountAbstraction); + } + + #[Test] + public function it_gets_a_blockchain_wallet(): void + { + $mockedWallet = [ + 'id' => 'bw_000000000000', + 'name' => 'Wallet Display Name', + 'network' => 'polygon', + 'address' => '0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', + 'signature_tx_hash' => '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', + 'is_account_abstraction' => false, + 'receiver_id' => 're_000000000000', + ]; + + $this->mockResponse($mockedWallet); + + $input = new GetBlockchainWalletInput( + receiverId: 're_000000000000', + id: 'bw_000000000000' + ); + + $response = $this->blindpay->wallets->blockchain->get($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('bw_000000000000', $response->data->id); + $this->assertEquals('Wallet Display Name', $response->data->name); + $this->assertEquals(Network::POLYGON, $response->data->network); + $this->assertEquals('0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C', $response->data->address); + } + + #[Test] + public function it_deletes_a_blockchain_wallet(): void + { + $this->mockResponse(['data' => null]); + + $input = new DeleteBlockchainWalletInput( + receiverId: 're_000000000000', + id: 'bw_000000000000' + ); + + $response = $this->blindpay->wallets->blockchain->delete($input); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } +} diff --git a/tests/Resources/Wallets/OfframpWalletsTest.php b/tests/Resources/Wallets/OfframpWalletsTest.php new file mode 100644 index 0000000..105113a --- /dev/null +++ b/tests/Resources/Wallets/OfframpWalletsTest.php @@ -0,0 +1,161 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_lists_offramp_wallets(): void + { + $mockedOfframpWallets = [ + [ + 'id' => 'ow_000000000000', + 'external_id' => 'your_external_id', + 'instance_id' => 'in_000000000000', + 'receiver_id' => 're_000000000000', + 'bank_account_id' => 'ba_000000000000', + 'network' => 'tron', + 'address' => 'TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb', + 'created_at' => '2021-01-01T00:00:00Z', + 'updated_at' => '2021-01-01T00:00:00Z', + ], + ]; + + $this->mockResponse($mockedOfframpWallets); + + $input = new ListOfframpWalletsInput( + receiverId: 're_000000000000', + bankAccountId: 'ba_000000000000' + ); + + $response = $this->blindpay->wallets->offramp->list($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('ow_000000000000', $response->data[0]->id); + $this->assertEquals('your_external_id', $response->data[0]->externalId); + $this->assertEquals('in_000000000000', $response->data[0]->instanceId); + $this->assertEquals('re_000000000000', $response->data[0]->receiverId); + $this->assertEquals('ba_000000000000', $response->data[0]->bankAccountId); + $this->assertEquals('tron', $response->data[0]->network); + $this->assertEquals('TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb', $response->data[0]->address); + } + + #[Test] + public function it_creates_an_offramp_wallet(): void + { + $mockedOfframpWallet = [ + 'id' => 'ow_000000000000', + 'external_id' => 'your_external_id', + 'network' => 'tron', + 'address' => 'TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb', + ]; + + $this->mockResponse($mockedOfframpWallet); + + $input = new CreateOfframpWalletInput( + receiverId: 're_000000000000', + bankAccountId: 'ba_000000000000', + externalId: 'your_external_id', + network: 'tron' + ); + + $response = $this->blindpay->wallets->offramp->create($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ow_000000000000', $response->data->id); + $this->assertEquals('your_external_id', $response->data->externalId); + $this->assertEquals('tron', $response->data->network); + $this->assertEquals('TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb', $response->data->address); + } + + #[Test] + public function it_gets_an_offramp_wallet(): void + { + $mockedOfframpWallet = [ + 'id' => 'ow_000000000000', + 'external_id' => 'your_external_id', + 'instance_id' => 'in_000000000000', + 'receiver_id' => 're_000000000000', + 'bank_account_id' => 'ba_000000000000', + 'network' => 'tron', + 'address' => 'TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb', + 'created_at' => '2021-01-01T00:00:00Z', + 'updated_at' => '2021-01-01T00:00:00Z', + ]; + + $this->mockResponse($mockedOfframpWallet); + + $input = new GetOfframpWalletInput( + id: 'ow_000000000000', + bankAccountId: 'ba_000000000000', + receiverId: 're_000000000000' + ); + + $response = $this->blindpay->wallets->offramp->get($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('ow_000000000000', $response->data->id); + $this->assertEquals('your_external_id', $response->data->externalId); + $this->assertEquals('in_000000000000', $response->data->instanceId); + $this->assertEquals('re_000000000000', $response->data->receiverId); + $this->assertEquals('ba_000000000000', $response->data->bankAccountId); + $this->assertEquals('tron', $response->data->network); + $this->assertEquals('TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb', $response->data->address); + } +} diff --git a/tests/Resources/Webhooks/WebhooksTest.php b/tests/Resources/Webhooks/WebhooksTest.php new file mode 100644 index 0000000..e3038e0 --- /dev/null +++ b/tests/Resources/Webhooks/WebhooksTest.php @@ -0,0 +1,153 @@ +mockHandler = new MockHandler; + $handlerStack = HandlerStack::create($this->mockHandler); + $httpClient = new Client(['handler' => $handlerStack]); + + $this->blindpay = new BlindPay( + apiKey: 'test-key', + instanceId: 'in_000000000000' + ); + + $this->injectHttpClient($httpClient); + } + + private function injectHttpClient(Client $client): void + { + $reflection = new ReflectionClass($this->blindpay); + $property = $reflection->getProperty('httpClient'); + $property->setAccessible(true); + $property->setValue($this->blindpay, $client); + } + + private function mockResponse(array $body, int $status = 200): void + { + $this->mockHandler->append( + new Response( + $status, + ['Content-Type' => 'application/json'], + json_encode($body) + ) + ); + } + + #[Test] + public function it_creates_a_webhook_endpoint(): void + { + $mockedWebhookEndpoint = [ + 'id' => 'we_000000000000', + ]; + + $this->mockResponse($mockedWebhookEndpoint); + + $input = new CreateWebhookEndpointInput( + url: 'https://example.com/webhook', + events: [WebhookEvents::RECEIVER_NEW] + ); + + $response = $this->blindpay->instances->webhookEndpoints->create($input); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('we_000000000000', $response->data->id); + } + + #[Test] + public function it_lists_webhook_endpoints(): void + { + $mockedWebhookEndpoints = [ + [ + 'id' => 'we_000000000000', + 'url' => 'https://example.com/webhook', + 'events' => ['receiver.new'], + 'last_event_at' => '2024-01-01T00:00:00.000Z', + 'instance_id' => 'in_000000000000', + 'created_at' => '2021-01-01T00:00:00Z', + 'updated_at' => '2021-01-01T00:00:00Z', + ], + ]; + + $this->mockResponse($mockedWebhookEndpoints); + + $response = $this->blindpay->instances->webhookEndpoints->list(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertIsArray($response->data); + $this->assertCount(1, $response->data); + $this->assertEquals('we_000000000000', $response->data[0]->id); + $this->assertEquals('https://example.com/webhook', $response->data[0]->url); + $this->assertCount(1, $response->data[0]->events); + $this->assertEquals(WebhookEvents::RECEIVER_NEW, $response->data[0]->events[0]); + $this->assertEquals('2024-01-01T00:00:00.000Z', $response->data[0]->lastEventAt); + $this->assertEquals('in_000000000000', $response->data[0]->instanceId); + } + + #[Test] + public function it_deletes_a_webhook_endpoint(): void + { + $this->mockResponse(['data' => null]); + + $response = $this->blindpay->instances->webhookEndpoints->delete('we_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertIsArray($response->data); + $this->assertArrayHasKey('data', $response->data); + $this->assertNull($response->data['data']); + } + + #[Test] + public function it_gets_webhook_endpoint_secret(): void + { + $mockedWebhookSecret = [ + 'key' => 'whsec_000000000000', + ]; + + $this->mockResponse($mockedWebhookSecret); + + $response = $this->blindpay->instances->webhookEndpoints->getSecret('we_000000000000'); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('whsec_000000000000', $response->data->key); + } + + #[Test] + public function it_gets_webhook_portal_access_url(): void + { + $mockedWebhookUrl = [ + 'url' => 'https://example.com/webhook', + ]; + + $this->mockResponse($mockedWebhookUrl); + + $response = $this->blindpay->instances->webhookEndpoints->getPortalAccessUrl(); + + $this->assertTrue($response->isSuccess()); + $this->assertNull($response->error); + $this->assertEquals('https://example.com/webhook', $response->data->url); + } +}