Giter VIP home page Giter VIP logo

node-iap's Introduction

In-app purchase verification

Inspired by the iap_verifier CoffeeScript module written by Paul Crawford, I wanted a pure JavaScript implementation of in-app purchase verification. I also wanted to add support for other app stores, and not just limit this to Apple. The iap module is exactly that. Pull requests to add support for other platforms are very welcome!

Installation

npm install iap

Usage

Initialisation

var iap = require('iap');

var platform = 'apple';
var payment = {
	receipt: 'receipt data', // always required
	productId: 'abc',
	packageName: 'my.app',
	secret: 'password',
	subscription: true,	// optional, if google play subscription
	keyObject: {}, // required, if google
	userId: 'user id', // required, if amazon
	devToken: 'developer id' // required, if roku
};

Purchase verification ( all platforms )

A method is exposed to verify purchase receipts:

iap.verifyPayment(platform, payment, function (error, response) {
	/* your code */
});

Or, if you prefer a promise-based alternative:

iap.verifyPayment(platform, payment)
.then(
    response => {	
        /* your code */ 
    },
    error => {
        /* your code */ 
    }
)

The receipt you pass must conform to the requirements of the backend you are verifying with. Read the next chapter for more information on the format.

Subscription cancellation ( Google Play only )

Google exposes an API for server side cancellation of recurring suscriptions. This might be be used to enable users to easily cancel subscriptions from within the app ( without going to Play Store ), or to allow subscriptions to be canceled by support people when users request it ( when for some reason the users are unable or unwilling to do so themselves).

iap.cancelSubscription("google", payment, function (error, response) {
	/* your code */
});

Or, if you prefer a promise-based alternative:

iap.cancelSubscription(platform, payment)
.then(
    response => {    
        /* your code */ 
    },
    error => {
        /* your code */ 
    }
)

Supported platforms

Amazon

The payment object

The receipt string represents the transaction returned from a channel or product purchase.

A Shared secret and user ID is required.

The response

The response passed back to your callback will also be Amazon specific. The entire parsed receipt will be in the result object:

{
	"receipt": {
		"betaProduct": false,
		"cancelDate": null,
		"parentProductId": null,
		"productId": "com.amazon.iapsamplev2.gold_medal",
		"productType": "CONSUMABLE",
		"purchaseDate": 1399070221749,
		"quantity": 1,
		"receiptId": "wE1EG1gsEZI9q9UnI5YoZ2OxeoVKPdR5bvPMqyKQq5Y=:1:11",
		"renewalDate": null,
		"term": null,
		"termSku": null,
		"testTransaction": false
	},
	"transactionId": "wE1EG1gsEZI9q9UnI5YoZ2OxeoVKPdR5bvPMqyKQq5Y=:1:11",
	"productId": "com.amazon.iapsamplev2.gold_medal",
	"platform": "amazon"
}

Apple

The payment object

The receipt string passed may be either the base64 string that Apple really wants, or the decoded receipt as returned by the iOS SDK (in which case it will be automatically base64 serialized).

Both productId and packageName (bundle ID) are optional, but when provided will be tested against. If the receipt does not match the provided values, an error will be returned.

To verify auto-renewable subscriptions you need to provide secret field that contains your In-App Purchase Shared Secret

Apple supports returning only the most recent transaction for auto-renewable subscriptions via their exclude-old-transactions option. This can greatly save on bandwidth for users that have more than one transaction. To enable this, pass excludeOldTransactions on the payment object:

let payment = {
  ...
  excludeOldTransactions: true
}

The response

The response passed back to your callback will also be Apple specific. The entire parsed receipt will be in the result object. Applications that support monthly and yearly subscription access will represent auto-renewable terms in either the in_app or latestReceiptInfo property.

{
	"receipt": {
		"original_purchase_date_pst": "2016-10-29 15:46:57 America/Los_Angeles",
		"purchase_date_ms": "1477802802000",
		"unique_identifier": "78abf2209323434771637ee22f0ee8b8341f14b4",
		"original_transaction_id": "120000257973875",
		"bvrs": "0.0.1",
		"transaction_id": "120000265421254",
		"quantity": "1",
		"unique_vendor_identifier": "206FED24-2EAB-4FC6-B946-4AF61086DF21",
		"item_id": "820817285",
		"product_id": "abc",
		"purchase_date": "2016-10-29 22:46:57 Etc/GMT",
		"original_purchase_date": "2016-10-29 22:46:57 Etc/GMT",
		"purchase_date_pst": "2016-10-29 15:46:57 America/Los_Angeles",
		"bid": "test.myapp",
		"original_purchase_date_ms": "1477781217000",
		"in_app": [
			{
				"quantity": "1",
				"product_id": "abc",
				"transaction_id": "120000265421254",
				"original_transaction_id": "120000257973875",
				"purchase_date": "2016-10-30 04:46:42 Etc/GMT",
				"purchase_date_ms": "1477802802000",
				"purchase_date_pst": "2016-10-29 21:46:42 America/Los_Angeles",
				"original_purchase_date": "2016-10-29 22:46:57 Etc/GMT",
				"original_purchase_date_ms": "1477781217000",
				"original_purchase_date_pst": "2016-10-29 15:46:57 America/Los_Angeles",
				"expires_date": "2016-11-30 05:46:42 Etc/GMT",
				"expires_date_ms": "1480484802000",
				"expires_date_pst": "2016-11-29 21:46:42 America/Los_Angeles",
				"web_order_line_item_id": "820817285",
				"is_trial_period": "false"
			},
		]
	},
	"latestReceiptInfo": [
		{
			"quantity": "1",
			"product_id": "abc",
			"transaction_id": "120000233230473",
			"original_transaction_id": "120000233230473",
			"purchase_date": "2016-06-12 22:36:58 Etc/GMT",
			"purchase_date_ms": "1465771018000",
			"purchase_date_pst": "2016-06-12 15:36:58 America/Los_Angeles",
			"original_purchase_date": "2016-06-12 22:36:58 Etc/GMT",
			"original_purchase_date_ms": "1465771018000",
			"original_purchase_date_pst": "2016-06-12 15:36:58 America/Los_Angeles",
			"expires_date": "2016-07-12 22:36:58 Etc/GMT",
			"expires_date_ms": "1468363018000",
			"expires_date_pst": "2016-07-12 15:36:58 America/Los_Angeles",
			"web_order_line_item_id": "120000034778618",
			"is_trial_period": "true"
		},
	],
	"pendingRenewalInfo": [
		{
		  "expiration_intent": "1",
 			"auto_renew_product_id": "abc",
			"original_transaction_id": "120000233230473",
			"is_in_billing_retry_period": "0",
			"product_id": "abc",
			"auto_renew_status": "0"
		}
	],
	"transactionId": "120000233230473",
	"productId": "abc",
	"platform": "apple",
	"environment": "production"
}

Google Play

The payment object

The receipt string is the purchase token that Google Play returns to the mobile application when a purchase is made.

Both packageName and productId are compulsory.

Lastly you must provide keyObject which is the Google API Service Account JSON key file linked to your Google Play account for authentication. This property can be either a string, file buffer or an object. If provided a string or file buffer, the call will automatically parse it into an object for use.

The response

The response passed back to your callback will also be Google Play specific. The entire parsed response will be in the receipt sub-object.

{
        "receipt": {
                "kind": "androidpublisher#productPurchase",
                "purchaseTimeMillis": "1410835105408",
                "purchaseState": 0,
                "consumptionState": 1,
                "developerPayload": ""
        },
        "transactionId": "ghbbkjheodjokkipdmlkjajn.AO-J1OwfrtpJd2fkzzZqv7i107yPmaUD9Vauf9g5evoqbIVzdOGYyJTSEMhSTGFkCOzGtWccxe17dtbS1c16M2OryJZPJ3z-eYhEJYiSLHxEZLnUJ8yfBmI",
        "productId": "abc",
        "platform": "google"
}

Roku

The receiept string represents the transaction returned from a channel or product purchase.

A developer ID is required.

The response

The response passed back to your callback will also be Roku specific. The entire parsed receipt will be in the result object:

{
	"receipt": {
		"errorCode": null,
		"errorDetails": null,
		"errorMessage": "",
		"status": 0,
		"amount": 4.99,
		"cancelled": false,
		"channelId": 70391,
		"channelName": "abc",
		"couponCode": null,
		"currency": "usd",
		"expirationDate": 1488337344000,
		"originalPurchaseDate": 1483153344000,
		"partnerReferenceId": null,
		"productId": "5KAZUPGB.0RF0",
		"productName": "BASIC - US MONTHLY",
		"purchaseDate": 1483153344000,
		"quantity": 1,
		"rokuCustomerId": "5e56c4c4d7d1504f813c630c2790e54a",
		"tax": 0,
		"total": 0,
		"transactionId": "380e9932-ed9a-48e8-bd66-a6ec00b5efd1"
	},
	"transactionId": "380e9932-ed9a-48e8-bd66-a6ec00b5efd1",
	"productId": "abc",
	"platform": "roku"
}

All Platforms

Regardless of the platform used, besides the platform-specific receipt, the following properties will be included:

Property Type Description
receipt object Data returned by platforms
platform string One of: 'apple', 'google', 'amazon', 'roku'
productId string Id of the product
transactionId string Id to uniquely identify transaction
purchaseDate int Date of purchase in millis
expirationDate int Date of expiration in millis

License

MIT

References

Amazon References

Code Inspiration

API Reference

Apple References

Code Inspiration

API Reference

Google Play References

Code Inspiration

API Reference

Receipt Generation

Roku References

API Reference

node-iap's People

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  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  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

node-iap's Issues

[Apple] Sandbox testing no longer works

When I try and do a sandbox test with iOS, I receive the following error:

error { Error: getaddrinfo ENOTFOUND buy.itunes.apple.com buy.itunes.apple.com:443
    at errnoException (dns.js:28:10)
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:76:26)
  code: 'ENOTFOUND',
  errno: 'ENOTFOUND',
  syscall: 'getaddrinfo',
  hostname: 'buy.itunes.apple.com',
  host: 'buy.itunes.apple.com',
  port: 443 }

Do not request access token with each verification request

Currently it seems that an access token for Google APIs is requested with each payment verification. This is unnecessary extra load to verify a single receipt and could cause significant delays when verifying a large amount of payments.

Create verifyPayment function that returns a Promise, instead of using callbacks

Since the function verifyPayment only works with callback, when I try to check the payment with the Apple store, when it responds and I can't return the result from the controller to the router, in order to send a response with it to the client.

Router code:

router.get('/subscriptionCheck', verifyToken, async function(req, res) {
    try {
        const subscription = await subscriptionController.getSubscription(req.userId);

        if (!subscription) return res.status(404).send('Failed to get subscription.');

        const response = await subscriptionController.checkSubscription(subscription.receipt);

        if(response.active){
            return res.status(200).json(response.data);
        } else {
            return res.status(400).json(response.data);
        }
    } catch (error) {
        return res.status(500).json({error: error.message});
    }
 });

Controller code:

 async function checkSubscriptionIOS(receipt, productId) {
    const payment = {
        receipt: receipt,
        productId: productId,
        packageName: 'bundleId',
        secret: 'secret',
    };

    return iap.verifyPayment('apple', payment, function(error, response) {
        if(error) {
            console.log("verify Apple subscriptions error:\n", error);

            return {active: false, data: error};
        } else {
            if (response.receipt.in_app.expires_date_ms < new Date().getTime()) {
                return {active: false, data: response};
            }

            return {active: true, data: response};
        }
    });
 }

[Apple] return receipt for expired subscriptions

If the subscription has expired (21006), the response indeed contains a valid decoded receipt.
I think it would make sense to return the decoded receipt rather than throwing an error.
What do you guys think?

Bump and release new version

Hey can you guys release a new version with the excludeOldTransactions option that was merged recently?

Thanks!

Having a trouble with google verification

Hey, I am trying to verify a payment that has done in Unity, but I keep getting Error 400 - Invalid Value. I am quite sure I've followed your instructions on how to use it but it seems like I've missed something. Here's my code snippet:
image

And this is the result of this request:
image

Could you please help me see what's wrong here? Thanks in advance.

Not working for google order with loop

for (i=0.....){
var payment.receipt = receipt(i);
iap.verifyPayment('google', payment, function (error, response) {
if(!error && response){
callback(response);
}
});
}
i m passing diffrant receipt each time but it return same responce for every recipt.
note: all responce consist copy of first itration.

return values

I am trying out google verification.

if the purchase is not yet consumed, i get an error that it is not yet consumed.
if the purchase is consumed, then i get the proper response.
if the purchase does not exist, i get an error but blank.

Is this the expected behavior? I just find it weird

Update minimist dependency

npm audit currently fails due to minimist not being up-to-date:

                       === npm audit security report ===                        
                                                                                
┌──────────────────────────────────────────────────────────────────────────────┐
│                                Manual Review                                 │
│            Some vulnerabilities require your attention to resolve            │
│                                                                              │
│         Visit https://go.npm.me/audit-guide for additional guidance          │
└──────────────────────────────────────────────────────────────────────────────┘
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Low           │ Prototype Pollution                                          │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package       │ minimist                                                     │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in    │ >=0.2.1 <1.0.0 || >=1.2.3                                    │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ iap                                                          │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path          │ iap > minimist                                               │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info     │ https://npmjs.com/advisories/1179                            │
└───────────────┴──────────────────────────────────────────────────────────────┘
found 1 low severity vulnerability in 2492 scanned packages
  1 vulnerability requires manual review. See the full report for details.

shared secret

Hi,

I am trying to make this work with the itunes shared secret. How to do ?

Thanks !

How to check if the receipt is valid

This node package looks great and I think I will use it, however the documenation doesn't specify how we know whether the receipt is valid or not (i.e there is no isValid or the likes of it in the response). Should I just check whether error is null to know if the receipt is valid?

Receipt returned by apple is different format

Hi!

While using your library I couldn't validate purchases, so I dug in the code and noticed that the receipt contained in the response from apple verification url isn't the one expected by the library.

-the bundle id is in receipt.bundle_id property instead of receipt.bid
-transaction_id and product_id are contained in receipt.in_app[0] instead of directly in the receipt.

I don't know why these values where located at different places for me, so I changed the code to test and made it work now.

I'll make a pull request to show you what I did and you can include it in the project if you want.

Feature to fetch google voided transactions

before i do a pull request, can you guys check if you think this can be added into this project?

https://github.com/kelchy/node-iap

it is not directly related to verifying payments so i am not sure. however, this is something important
to developers offering inapp purchases. recently we got a lot of cheaters who made a lot of refunds.
this would help everyone in the long run.

i am not sure how you guys want to format the readme for this so asking for help.

Not sure sorting by `transaction_id` works with family sharing

Noticed that a iOS user who was having receipt validation issues had two items in the latest receipt info array:

  "latest_receipt_info": [
    {
      "quantity": "1",
      "product_id": "yearly",
      "transaction_id": "501136461303889853",
      "original_transaction_id": "501136461303889853",
      "purchase_date": "2022-02-10 17:59:20 Etc/GMT",
      "purchase_date_ms": "1644515960000",
      "purchase_date_pst": "2022-02-10 09:59:20 America/Los_Angeles",
      "original_purchase_date": "2022-02-10 17:59:21 Etc/GMT",
      "original_purchase_date_ms": "1644515961000",
      "original_purchase_date_pst": "2022-02-10 09:59:21 America/Los_Angeles",
      "expires_date": "2022-02-17 17:59:20 Etc/GMT",
      "expires_date_ms": "1645120760000",
      "expires_date_pst": "2022-02-17 09:59:20 America/Los_Angeles",
      "cancellation_date": "2022-03-10 20:57:06 Etc/GMT",
      "cancellation_date_ms": "1646945826586",
      "cancellation_date_pst": "2022-03-10 12:57:06 America/Los_Angeles",
      "is_trial_period": "true",
      "is_in_intro_offer_period": "false",
      "cancellation_reason": "0",
      "in_app_ownership_type": "FAMILY_SHARED"
    },
    {
      "quantity": "1",
      "product_id": "monthly",
      "transaction_id": "150001161180179",
      "original_transaction_id": "150001161180179",
      "purchase_date": "2022-08-12 19:06:35 Etc/GMT",
      "purchase_date_ms": "1660331195000",
      "purchase_date_pst": "2022-08-12 12:06:35 America/Los_Angeles",
      "original_purchase_date": "2022-08-12 19:06:35 Etc/GMT",
      "original_purchase_date_ms": "1660331195000",
      "original_purchase_date_pst": "2022-08-12 12:06:35 America/Los_Angeles",
      "expires_date": "2022-09-12 19:06:35 Etc/GMT",
      "expires_date_ms": "1663009595000",
      "expires_date_pst": "2022-09-12 12:06:35 America/Los_Angeles",
      "is_trial_period": "false",
      "is_in_intro_offer_period": "false",
      "in_app_ownership_type": "PURCHASED"
    }
  ]

Our tactic of grabbing the last item in the array failed, because iap sorts the items before returning them:

latestReceiptInfo = result.latest_receipt_info.sort(function (a, b) {

However the sorting is via transaction_id and in this case the first transaction has a much larger id than the second. Not sure if related to family sharing, or a red herring.

Perhaps don't sort the array when parsing?

iOS validation not working

While I'm using this library for Google validation with no problems, I can't find a way to make it work with Apple.

I'm getting the receipt as Apple's documentation says:
To retrieve the receipt data, use the appStoreReceiptURL method of NSBundle to locate the app’s receipt, and then read the entire file. If the appStoreReceiptURL method is not available, you can fall back to the value of a transaction's transactionReceipt property for backward compatibility.

The result is a VERY LOOONG string:
MIIT3AYJKoZIhvcNAQcCoIITzTCCE8kCAQExCzAJBgUrDgMCGgUAMIIDjQYJKoZIhvcNAQcBoIIDfgSCA3oxggN2MAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQECAQEEAwIBADALAgELAgEBBAMCAQAwCwIBDgIBAQQDA... //truncated for obvious reasons

That I'm passing AS IS as the receipt string:
The receipt string passed may be either the base64 string that Apple really wants, or the decoded receipt as returned by the iOS SDK (in which case it will be automatically base64 serialized).

I'm also passing productId and package name:
Both productId and packageName (bundle ID) are optional, but when provided will be tested against. If the receipt does not match the provided values, an error will be returned.

The call fails with this message:
The data in the receipt-data property was malformed or missing.

Is it something that I'm doing wrong?

Thanks,
Walt

Determining whether Google purchase was a test transaction

Hi, I have this set up and working but would also like to use it to know whether a purchase was a test transaction or not. Capable of doing this with the response.environment on iOS, but no way to do it in Android. Would it be possible to update the code that hits the server to also fetch the "orderId" from Google, or anything else that can note whether it was a sandbox/test transaction?

How to get google key object file?

Thanks a lot for this library.
I use it for verify google play subscribe.
But I don't know how to get the keyObject.

var payment = {
       //xxxxx
	keyObject: {}, // required, if google
};

Someone can help me? Thanks.

Remove response assertions?

When a receipt is sent from an app to the server, I need to perform some actions, and then respond to the app that it's ok to consume the purchase.

The problem is that this module is asserting that the purchase is already consumed. That's not safe, because if the app buys then consumes the purchase before notifying the server, and then something goes wrong, the purchase has been (more or less) lost in the ether, since it will no longer appear in the list of purchases from Google after being consumed but will not have registered with the node server.

Any chance that line (along with the purchaseState line, in case we actually need to check on that info) could simply be removed? Or if you have some external code depending on that check being there, add an alternative method for just checking the state of a purchase rather than verifying those exact conditions?

Thanks!

Not working for Google

For apple order it's it's working fine but for google order, the response is undefined also it's not giving any error. i m using below configuration.Can you please help me out.

var platform = 'google';
var payment = {
receipt: '', // always required
productId: 'monthly_subscription',
packageName: 'com..io',
keyObject:'../
.json'
};

Thanks

Roku verification release

Hello,

The Roku payment verification is not yet released on npm. When do you intend to push a new version of iap which contains this?

Thanks.

[Google] - use oderId instead of the token as transationId

orderId not the token should be the property used as transactionId

the Google token is more like the Apples base64 encoded receipt: both enable the developer to fetch updated information about users purchase/subscription. Googles orderId is the property which corresponds to Apples transaction_id.

its the orderId that uniquely identifies each transaction made by the user. What you are currently using as transactionId is the token which is used to fetch information from the api about given purchase/subscription. In case of renewing subscriptions, there will by more then one transaction associated with give token, each transaction having a unique orderId which corresponds to subsequent subscription periods. If you ever want to match data fetched from googles purchases api with play store sales & payout reports you will need to use the orderId not the token.

Checking if auto renewable subscription is valid..

Thank you for this library, but i would like to ask something since it's my first time implementing auto renewable subscription..

So now after the user purchase the subscription i should send the receipt to my backend and validate it, and then if it's valid i should store the receipt and the expiration date in my db, and since i have a corn job that runs daily i can check if the subscription is expired, and if it's i can send the receipt again to check if it's renewed or not, is that right? and if it returns an error meaning that it's not valid anymore?

app.post('/receipt-validation' , (request , response) => {

  const platform = request.body.platform;
  const userId = request.body.userId;
  const receipt = request.body.platform;

  const payment = {
    receipt: receipt, // always required
    productId: 'com.shamhandev.snaplearn',
    packageName: 'com.shamhandev.snaplearn',
    excludeOldTransactions: true, // ios
    secret: 'password',      
    subscription: true,	// optional, if google play subscription
    keyObject: {}, // required, if google
  };


  iap.verifyPayment(platform, payment)
  .then(
    res => {	
       //Updating subscription status in db
    },
    err => {
        /* your code */ 
    }
)



});

Update minimist

The minimist package had some serious security problems. Can you please update the dependencies.

# npm audit report

minimist  <=1.2.5
Severity: critical
Prototype Pollution in minimist - https://github.com/advisories/GHSA-xvch-5gv4-984h
Prototype Pollution in minimist - https://github.com/advisories/GHSA-vh95-rmgr-6w4m

node beginner: Google API key object must be provided

I get

{ [AssertionError: Google API key object must be provided]
name: 'AssertionError',
actual: undefined,
expected: true,
operator: '==',
message: 'Google API key object must be provided',
generatedMessage: false }

Where do i specify Google API key object?? you said in the document that we need keyObject but since I'm a node first time user, not sure what that means..

also for receipt ... can this be the order number (=transaction id) ?

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.