Git Product home page Git Product logo

sdk's Introduction

Latest Stable Version Total Downloads Latest Unstable Version Build Status Coverage Status

Pay.nl PHP SDK



Installation

This SDK uses composer.

Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.

For more information on how to use/install composer, please visit: https://github.com/composer/composer

To install the Pay.nl PHP sdk into your project, simply

$ composer require paynl/sdk

Installation without composer

If you don't have experience with composer, it is possible to use the SDK without using composer.

You can download the zip on the projects releases page.

  1. Download the package zip (SDKvx.x.x.zip).
  2. Unzip the contents of the zip, and upload the vendor directory to your server.
  3. In your project, require the file vendor/autoload.php
  4. You can now use the SDK in your project

Requirements

The PAY. PHP SDK is tested up to PHP version 8.2 and requires the PHP cURL extension.

Quick start and examples

Set the configuration

require __DIR__ . '/vendor/autoload.php';

# Replace tokenCode apitoken and serviceId with your own.
\Paynl\Config::setTokenCode('AT-####-####');
\Paynl\Config::setApiToken('****************************************');
\Paynl\Config::setServiceId('SL-####-####');

Get available payment methods

require __DIR__ . '/vendor/autoload.php';

\Paynl\Config::setTokenCode('AT-####-####');
\Paynl\Config::setApiToken('****************************************');
\Paynl\Config::setServiceId('SL-####-####');

$paymentMethods = \Paynl\Paymentmethods::getList();
var_dump($paymentMethods);

Start a transaction

require __DIR__ . '/vendor/autoload.php';

\Paynl\Config::setTokenCode('AT-####-####');
\Paynl\Config::setApiToken('****************************************');
\Paynl\Config::setServiceId('SL-####-####');

$result = \Paynl\Transaction::start(array(
    # Required
        'amount' => 10.00,
        'returnUrl' => Paynl\Helper::getBaseUrl().'/return.php',

    # Optional
    	'currency' => 'EUR',
        'exchangeUrl' => Paynl\Helper::getBaseUrl().'/exchange.php',
        'paymentMethod' => 10,
        'bank' => 1,
        'description' => 'demo betaling',
        'testmode' => 1,
        'extra1' => 'ext1',
        'extra2' => 'ext2',
        'extra3' => 'ext3',
        'products' => array(
            array(
                'id' => 1,
                'name' => 'een product',
                'price' => 5.00,
                'tax' => 0.87,
                'qty' => 1,
            ),
            array(
                'id' => 2,
                'name' => 'ander product',
                'price' => 5.00,
                'tax' => 0.87,
                'qty' => 1,
            )
        ),
        'language' => 'EN',
        'ipaddress' => '127.0.0.1',
        'invoiceDate' => new DateTime('2016-02-16'),
        'deliveryDate' => new DateTime('2016-06-06'), // in case of tickets for an event, use the event date here
        'enduser' => array(
            'initials' => 'T',
            'lastName' => 'Test',
            'gender' => 'M',
            'birthDate' => new DateTime('1990-01-10'),
            'phoneNumber' => '0612345678',
            'emailAddress' => '[email protected]',
        ),
        'address' => array(
            'streetName' => 'Test',
            'houseNumber' => '10',
            'zipCode' => '1234AB',
            'city' => 'Test',
            'country' => 'NL',
        ),
        'invoiceAddress' => array(
            'initials' => 'IT',
            'lastName' => 'ITEST',
            'streetName' => 'Istreet',
            'houseNumber' => '70',
            'zipCode' => '5678CD',
            'city' => 'ITest',
            'country' => 'NL',
        ),
    ));

# Save this transactionid and link it to your order
$transactionId = $result->getTransactionId();

# Redirect the customer to this url to complete the payment
$redirect = $result->getRedirectUrl();

On the return page, redirect the user to the thank you page or back to checkout

require __DIR__ . '/vendor/autoload.php';

\Paynl\Config::setTokenCode('AT-####-####');
\Paynl\Config::setApiToken('****************************************');

$transactionId = $_REQUEST['orderId'];

$transaction = \Paynl\Transaction::status($transactionId);

# Manual transfer transactions are always pending when the user is returned
if( $transaction->isPaid() || $transaction->isPending()) {
   # Redirect to thank you page
} elseif($transaction->isCanceled()) {
   # Redirect back to checkout
}

On the exchange script, process the order

require __DIR__ . '/vendor/autoload.php';

\Paynl\Config::setTokenCode('AT-####-####');
\Paynl\Config::setApiToken('****************************************');

$transactionId = $_REQUEST['order_id'];

$transaction = \Paynl\Transaction::status($transactionId);

if($transaction->isPaid() || $transaction->isAuthorized()) {
    # Process the payment
} elseif($transaction->isCanceled()) {
    # Payment canceled, restock items
}

# Always respond with TRUE|
echo "TRUE| ";

# Optionally you can send a message after TRUE|, you can view these messages in the logs.
# https://admin.pay.nl/logs/payment_state
echo ($transaction->isPaid() || $transaction->isAuthorized())?'Paid':'Not paid';

Failover gateway

In the event of an outage, set the failover gateway like this:

use Paynl\Config;
use Paynl\Transaction;

require __DIR__ . '/vendor/autoload.php';

Config::setTokenCode('AT-####-####');
Config::setApiToken('****************************************');
Config::setServiceId('SL-####-####');

# Setting Failover gateway (for available cores, call Config::getCores())
Config::setCore( Config::CORE2 );

# Or for SDK versions lower then 1.6.7, use:
Config::setApiBase('https://rest.achterelkebetaling.nl');

$requiredArguments = []; // See: Start a transaction example
$result = Transaction::start($requiredArguments);

Testing

Please run vendor/bin/phpunit --bootstrap vendor/autoload.php tests/ to test the application

sdk's People

Contributors

andypieters avatar annevelden avatar backendtea avatar bbrunekreeft avatar chris-kruining avatar curry684 avatar czim avatar daan-pay avatar fruitl00p avatar grendel7 avatar grisgruis avatar ivowams avatar jamalo avatar jornbakery avatar jstjanssens avatar kevinverschoor avatar manuelderuiter avatar marcelschoolenberg avatar marius-123 avatar michael-pay avatar patrickkivits avatar rapid0o avatar ssl avatar villhaber avatar woutse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sdk's Issues

Starting a payment generates 100s of transaction IDs

Using the code under "Start a transaction", I initiate a payment and retrieve a transaction ID.
I'm currently testing my setup. All I have on this payment processing page is this:

require_once("pay.nl/autoload.php");

\Paynl\Config::setTokenCode('{{code}}');
\Paynl\Config::setApiToken('{{token}}');
\Paynl\Config::setServiceId('{{serviceId}}');

$result = \Paynl\Transaction::start(array(
    // required
        'amount' => 10.00,
        'returnUrl' => Paynl\Helper::getBaseUrl().'/sandbox/return.php',

    // optional
    	'currency' => 'EUR',
        'exchangeUrl' => Paynl\Helper::getBaseUrl().'/sandbox/exchange.php',
    ));

// Save this transactionid and link it to your order
$transactionId = $result->getTransactionId();

mail("{{email}}", "Payment started", $transactionId);

// Redirect the customer to this url to complete the payment
$redirect = $result->getRedirectUrl();	

header("Location: ".$redirect);

The redirect works, I'm taken to a pay.nl page for payment.

However, I'm also receiving 100s (about 1000 at the current count) emails with the transaction ID (as per the mail that should be sent from the code above). However, each transaction ID is different, so the page in question is being called 100s of times.
What could be causing this?

Failure when receiving data from the peer (CURLE_RECV_ERROR): OpenSSL SSL_read: Connection reset by peer, errno 104

Hello,

I'm sometimes receiving this error when receiving webhooks. Is this an hick-up in the API or something wrong on my side?

Failure when receiving data from the peer (CURLE_RECV_ERROR): OpenSSL SSL_read: Connection reset by peer, errno 104

Paynl\Error\Error: Failure when receiving data from the peer (CURLE_RECV_ERROR): OpenSSL SSL_read: Connection reset by peer, errno 104 in /vendor/paynl/sdk/src/Api/Api.php:77
Stack trace:
#0 /vendor/paynl/sdk/src/Api/DirectDebit/DebitGet.php(44): Paynl\Api\Api->doRequest()
#1 /vendor/paynl/sdk/src/DirectDebit.php(91): Paynl\Api\DirectDebit\DebitGet->doRequest()
#2 /app/Libraries/DirectDebit/PayNLDirectDebit.php(98): Paynl\DirectDebit::get()

Confusing result for transaction refund

The documentation and SDK logic of the handling of refunds is confusing.

In the Refund result class, references are made, in methods like Refund::getRefundAmount, to method Refund::getRefundedTransaction that use it like an array, while the docblock indicates that it is a string. Is it an array?

In the documentation, there is no specific Transaction::Refund documentation page for the API, so I would expect the request & response for refunds on this page to describe what's going on:
https://docs.pay.nl/developers#refunds

However, this does not include the fields that the SDK refers to; the SDK does seem to simply take the response content as is and feed it directly into the result class mentioned under point 1. Which is correct? The SDK or the documentation? Or am I missing something?

Transaction parameters do not match SDK Docs

This package uses different parameters then described in https://docs.pay.nl/developers#transactions-start and https://docs.pay.nl/developers#instore-start

Examples:

SDKDocs PHP SDK
finishUrl returnUrl
paymentOptionId paymentOption
paymentOptionSubId bank
amount in cents amount in decimal value

This is very unclear and is not mentioned in either the docs or this package. But for an official SDK, it would be expected to follow the official SDK docs.

I understand from #1 that this is intentional, but imho that should either belong in a seperate package (like https://github.com/paynl/omnipay-paynl) or you should transparently allow the options. Eg. when settings returnUrl, you can internally set finishUrl.
This only doesn't work for the amount obviously, and changing it now would be a big BC break.

GetForExchange doesn't accept JSON

Hey there,

There is a problem with the GetForExchange function. The problem resides in Transaction.php and affects users that selected the option for return json.

A viable solution to the problem would be the following:

public static function getForExchange()
    {
        if (isset($_GET['order_id'])) {
            return self::get($_GET['order_id']);
        }
        if (isset($_POST['order_id'])) {
            return self::get($_POST['order_id']);
        }

        //try to see if it's json
        $input = file_get_contents('php://input');

        $json = json_decode($input,true);
        if (json_last_error() == 0)
            return self::get($json['order_id']);

        $xml = simplexml_load_string($input);
        return self::get($xml->order_id);
    }

I did not have the time to do a PR, but i wanted to let you and others know that this is an issue.

Cheers

orderId missing when returning on returnUrl

I'm testing the API with this SDK (in testmode) and I noticed that when I come back to the returnUrl after completing the transaction no orderId nor other parameters are appended. Looking at my code (months old code that I haven't touched in a while) and at the README.md it should append an orderId right?

Am I missing something here?

Undefined index: refundId

Getting the RefundID after a refund gives me an Undefined index.
$result = \Paynl\Transaction::refund($transactionID, $amount, $description);
$refundID = $result->getRefundId();

throws
PHP Notice 'yii\base\ErrorException' with message 'Undefined index: refundId'
in /data/www/html/administratie/vendor/paynl/sdk/src/Result/Transaction/Refund.php:35

This stopped working after version 1.5.2

How do I test a transaction?

I want to test a payment, starting the transaction, 'paying', arriving at the return address, and processing the results at the exchange address.

I do not see how I can do this. How do I start a test transaction?

Serious memory leak issue in php-curl-class < 8.1.0

Just FYI: there is a serious memory leak issue in the php-curl-class before version 8.1.0 (fix was 2018-05-23)!

A reference to the class (including low-level curl handles) was kept in memory preventing release so you would run out of memory and/or connection handles if you keep PHP running as a daemon or something similar.
This will cause all sorts of weird problems which are hard to track down.

Transaction expire time

In the Paynl API you can set a property "expire" which indicates how many seconds it takes for a transaction to expire. How do i do this with the SDK? I saw that you can set "expireDate". However, the Paynl API says nothing about a field named "expireDate".

isCanceled returned true bij een refund

De isCanceled-functie checkt of de statuscode kleiner is dan 0 en returned dan true. Voor de statussen cancel (-90), denied (-63), expired (-80), etc. snap ik dat wel. Alleen voor de statussen refund (-81) en partial refund (-82) is dit wat minder intuïtief imo. Misschien een idee om een (of twee) extra functies toe te voegen zoals bijv. isRefunded? Dan zou er in de code aan de clientzijde een mooiere control-flow ontstaan, bijv.:

if($transaction->isPaid()) {

} elseif($transaction->isRefunded()) {

} elseif($transaction->isPending()) {

} elseif($transaction->isCanceled()) {

}

SDK still uses legacy endpoints?

We've been using this SDK for a few years now and everything seems to be working fine. However, I was reading the PayNL docs, and noticed that the SDK uses legacy endpoints only. Is there a reason for doing so?

image

PPM

Hi,

Is er een mogelijk om de pincode te checken voor PPM?

Fieldlengths ?

A quick question as I cant seem to find any information about this (yet): Is there a specification of fieldlengths? (i.e. max length of a lastname, firstname or any other field) somewhere? The current request just takes all the information as is without performing any pre-flight checks on the data...

Are there any plans to include some time of pre-flight validation?

Undefined index: description

/src/Api/Transaction/Refund.php throws an Undefined index: description at line 112.

a var_dump of $output shows this:

array(2) {
  ["request"]=>
  array(3) {
    ["result"]=>
    string(1) "0"
    ["errorId"]=>
    string(1) "8"
    ["errorMessage"]=>
    string(26) "{ERROR_REFUND_NOT_ALLOWED}"
  }
  ["refundId"]=>
  string(0) ""
}

authorization header support

Hi,

I would like to know if the API supports authorization headers.

I am making an API with is secured with JWT, So when starting a transaction I would like to create a token, pass it to the transaction start, and have it as an "Authorization: Bearer [jwt]" header for the return url.

Is this possible?

Testing transactions

Currently the transaction::start()-method actually sends the transaction to you in order to return a TransactionID. Even including the testmode=1 into the transaction the callback is still fired (and e-mails errors in regards to not being able to hit the callback)

Is there anyway to prevent these unnecessary e-mails (or to simulate the callback in someway manually?)

Exception when trying to print/send invoices manually

After placing an order, and trying to generate/send an invoice manually, Magento returns the following mention on the screen: "The invoice can't be saved at this time. Please try again later."

When looking at the exceptions log of Magento, the following shows up: "main.CRITICAL: 'TransactionId is niet geset' is required {"exception":"[object] (Paynl\\Error\\Required(code: 0): 'TransactionId is niet geset' is required at /vendor/paynl/sdk/src/Api/Transaction/Capture.php:68)"} []"

Any ideas why this is happening and how this can be fixed?

Unable to get orignal order amount from transaction/status

All functions to retrieve the paid amount look like this, where the value is divided by 100 before being passed.

Working with floats when dealing with money usually isn't a very good choice. Instead we prefer to use Money objects from either moneyPHP or brick money.
These however expect the full amount as an integer (with decimals as part of the number), or a decimal string.

Could you make the value parts of the API calls available through the SDK as well, instead of us having to rely on getting the underlying data array and retrieve the data from there ourselves.

    /**
     * @return float|int The original amount
     */
    public function getAmountOriginal()
    {
        return $this->data['paymentDetails']['amountOriginal']['value'] / 100;
    }

Verkeerde check op state in functie Transaction->isRefunded

In src/Result/Transaction/Transaction.php vanaf regel 79 staat het volgende:

    if ($this->data['paymentDetails']['state'] == 'REFUND') {
        return true;
    }

    if ($allowPartialRefunds && $this->data['paymentDetails']['stateName'] == 'PARTIAL_REFUND') {
        return true;
    }

Moet de eerste check niet checken op de stateName in plaats van de state? Volgens de API docs (en de rest van de code in het bestand) is state de status-code/integer, en stateName inderdaad de string.

Transaction Not Found when adding recurring

I am attempting to set up recurring payments. I successfully start a transaction, but when I try to add the transaction as recurring, I receive an error: "100 - Transaction not found".

Here is the code I'm using:

$startData['amount'] = 12.50;
$startData['returnUrl'] = dirname(\Paynl\Helper::getBaseUrl()) . '/return.php';
$startData['testmode'] = 1;

$result = \Paynl\Transaction::start($startData);
$transactionId = $result->getTransactionId();

$recurring = \Paynl\Transaction::addRecurring(array(
    'transactionId' => trim($transactionId),
    'amount' => 10,
    'description' => 'Your recurring payment',
    'extra1' => 'SDK'
));

Am I doing something wrong here? Is recurring only available for direct debits? Any help is appreciated!

Issue in version 1.2.7

We face an issue on version 1.2.7: Exception message: Notice: Undefined index: birthDate in public_html/vendor/paynl/sdk/src/Api/Transaction/Start.php on line 447. The transaction is canceled.

Add option for CURLOPT_CAINFO

Please add the option to set a certificate file for the curl request.

I have 2 options at this moment, but they are both bad practice:

  • Configure curl.cainfo file in php.ini
  • Edit the composer package

Without this any curl request to https:// fails. (And i won't disable "SSL_VERIFYPEER")

PAY-103 - Token not valid for this company

Getting a PAY-103 - Token not valid for this company error. Any ideas how company is checked? PAY_TOKEN is set as is SERVICE_ID so not sure what to do here. And have not found this error message either..

Alternatieve spelling canceled

In Paynl\Result\Transaction is de functie isCanceled aanwezig, een alternatieve spelling is met twee l'en (cancelled). Is het misschien een idee om een alias aan te maken die isCancelled heet? Wij gebruiken intern namelijk toevallig 'Cancelled' i.p.v. 'Canceled' waardoor je bijv. situaties krijgt als:

$transaction = \Paynl\Transaction::getForExchange();
if($transaction->isCanceled()) {
    $payment->setStatus('Cancelled');
}

Transaction refund result: undefined index refundId

After updating from version 1.5.12 to 1.5.13 the following error occured in my system:

 ErrorException  : Undefined index: refundId

  at /srv/http/www/vendor/paynl/sdk/src/Result/Transaction/Refund.php:19
    15|      * @return string The refundId
    16|      */
    17|     public function getRefundId()
    18|     {
  > 19|         return $this->data['refundId'];
    20|     }
    21| }
    22|

  Exception trace:

  1   Illuminate\Foundation\Bootstrap\HandleExceptions::handleError("Undefined index: refundId", "/srv/http/www//vendor/paynl/sdk/src/Result/Transaction/Refund.php", [])
      /srv/http/www/vendor/paynl/sdk/src/Result/Transaction/Refund.php:19

  2   Paynl\Result\Transaction\Refund::getRefundId()
      /srv/http/www/app/Booking.php:178

When I switch back to 1.5.12 everything works again.

getPaymentOptions en paymentProfileId

Is het mogelijk om de API methode getPaymentOptions (v2) te implementeren? Wij gebruiken dit voor de betalen met credits methode in jullie systeem. met getPaymentOptions halen we de verschillende prijzen/credits op, en geven deze dan mee terug als amount.

Edit: Zie dat getPaymentOptions niet werkt met serviceId, en de methode transaction\getServicePaymentOptions geeft ook niet de gewenste informatie, terwijl mij dat wel het gewenste gedrag lijkt.

PHP 7.2?

In the requirements, I read:

The Pay.nl PHP SDK works on php versions 5.3, 5.4, 5.5, 5.6, 7.0 and 7.1 Also the php curl extension needs to be installed.

My website runs on PHP 7.2.14. Is this going to be a problem?

missing gender in invoiceAddress on enduser

Seems that the 'gender' field on an invoice address is missing from being filled?

Also i'd like to recommend that the invoiceAddress be filled with $endUser fields before having specific endUser['invoiceAddress'] fields overwrite them...
That way one would only have to fill the different data (i.e. the actual address) but it would allow overwriting them with different and more specific values...

PSR-4 compatible

Deprecation Notice: Class Paynl\Result\Transaction\transactionDetails located in ./vendor/paynl/sdk/src/Result/Transaction/TransactionDetails.php does not comply with psr-4 autoloading standard. It will not autoload anymore in Composer v2.0. in /usr/share/php/Composer/Autoload/ClassMapGenerator.php:201

Renaming the classname from transactionDetails to TransactionDetails should fix the problem and makes the whole PSR-4 compatible.

Support for php 7.2, 73 etc

The readme currently states that this package supports up to php 7.1.

Could this package be updated to also support php 7.2 and higher? Preferably by also running tests on those versions of the language.

Documentation about `statsdata.object` confusing

As a partner of Pay.nl, we have received a request to add statsdata.object to the Pronamic Pay plugin so that Pay.nl can measure the number of transactions that are initiated via our plugin. What we noticed was that the documentation on this is somewhat confusing. We just had your partner manager Fréderique and a colleague visiting. They indicated that the documentation is very good. In this issue a number of things that we found confusing.

https://docs.pay.nl/developers#mandatory-data-technical-partners

Scherm­afbeelding 2022-12-01 om 14 56 51

The parameters is named object but is of the type string, why do you use the name object for this? For us it's confusing since object is also a (PHP) type.


On the Transaction:START documentation page: https://docs.pay.nl/developers#transaction-paylater this object parameter is not documented?

Scherm­afbeelding 2022-12-01 om 14 59 55

On this page you also mention statsData (camelCase) instead of statsdata. Are these the same parameters or are they different things?


Many of the statsData parameters are quite unclear to us, where do you use these parameters for?

  • promotorId
  • info
  • tool
  • transferData

In addition to https://docs.pay.nl/developers#transaction-paylater, we discovered https://developer.pay.nl/reference/post_transactions today via https://www.pay.nl/en/. There is an API https://rest.pay.nl/v1/ and an API https://rest-api.pay.nl/v16/? What is the story behind that and what should we use now?


On https://docs.pay.nl/developers?language=nl we also read the following:

Bij elk API verzoek dien je de versie van de functie aan te geven. Welke versies per functie beschikbaar zijn vind je in de API documentatie.

Scherm­afbeelding 2022-12-01 om 15 15 48

We don't see this in the documentation? We were still using version v4, but it is very difficult to see what has changed since then?


On https://developer.pay.nl/docs/glossary-1#stats-parameters we see the following:

Scherm­afbeelding 2022-12-01 om 15 18 26

On https://developer.pay.nl/reference/post_transactions we see:

Scherm­afbeelding 2022-12-01 om 15 19 23

Is the maximum length now 32 or 64 characters?

An error is returned when getList is called

When I try to use the example in the SDK, I get an error.

\Paynl\Config::setTokenCode('AT-####-####');
\Paynl\Config::setApiToken('****************************************');
\Paynl\Config::setServiceId('SL-####-####');
$paymentMethods = \Paynl\Paymentmethods::getList();
var_dump($paymentMethods);

return error:
HTTP/1.1 422 Unprocessable Entity

Please look at this for me. Thank you very much!

Start transaction vs API versions

After some back-and-forth with our client it seems the (current) SDK differs quite a bit (v5) with the (latest) API version (v7). Is there a comprehensive changelog between the two or will the SDK be updated to support the latest API version?

Having to juggle the version and possible changes manually seems error prone and difficult...

Is it possible to align the SDK releases with updates according to spec (https://admin.pay.nl/docpanel/api) ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.