Android In-App Integration Tutorial
Today I am going post about In-App Billing.
In-app products are the digital goods that you offer for sale from inside your application to users. Examples of digital goods includes in-game currency, application feature upgrades that enhance the user experience, and new content for your application.
Below i explained step by step. How to Integrate In-App Billing in Your Application.
*Note.
Don't Test In-App In emulator or unsigned application.
AndroidManifest.xml file you need to give permission
AndroidManifest.xml
str
= str.replace("%lang%", locale.getLanguage().toLowerCase());
str
= str.replace("%region%", locale.getCountry().toLowerCase());
Today I am going post about In-App Billing.
In-app products are the digital goods that you offer for sale from inside your application to users. Examples of digital goods includes in-game currency, application feature upgrades that enhance the user experience, and new content for your application.
Below i explained step by step. How to Integrate In-App Billing in Your Application.
*Note.
Don't Test In-App In emulator or unsigned application.
AndroidManifest.xml file you need to give permission
<uses-permission android:name="com.android.vending.BILLING"
/>
AndroidManifest.xml
<?xml version="1.0"
encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.vj.test"
android:versionCode="9"
android:versionName="1.9" >
<uses-permission android:name="com.android.vending.BILLING"
/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>
<application
android:icon="@drawable/icon"
android:label="@string/app_name" >
<activity
android:name="com.vj.test.Mainactivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN"
/>
<category android:name="android.intent.category.LAUNCHER"
/>
</intent-filter>
</activity>
<service android:name="com.vj.test.BillingService"
/>
<receiver android:name="com.vj.test.BillingReceiver"
>
<intent-filter>
<action android:name="com.android.vending.billing.IN_APP_NOTIFY"
/>
<action android:name="com.android.vending.billing.RESPONSE_CODE"
/>
<action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED"
/>
</intent-filter>
</receiver>
</application>
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
</manifest>
Then come to Activity class
here you need to add your in-app product ID
private static final CatalogEntry[] CATALOG = new CatalogEntry[] {
new CatalogEntry("Your Product
ID", R.string.abc123, Managed.MANAGED),
new CatalogEntry("Your Product
ID ", R.string.abc1234, Managed.MANAGED),
new CatalogEntry("Your Product
ID ", R.string.ab123200, Managed.MANAGED)
};
Full Source Code Activity Class
package com.vj.test;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import
android.app.Activity;
import
android.app.AlertDialog;
import android.app.Dialog;
import
android.content.Context;
import
android.content.DialogInterface;
import
android.content.Intent;
import
android.content.SharedPreferences;
import
android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import
android.text.SpannableStringBuilder;
import android.util.Log;
import android.view.View;
import
android.view.View.OnClickListener;
import
android.view.ViewGroup;
import
android.widget.AdapterView;
import
android.widget.AdapterView.OnItemSelectedListener;
import
android.widget.ArrayAdapter;
import
android.widget.Button;
import
android.widget.SimpleCursorAdapter;
import
android.widget.Spinner;
import
android.widget.Toast;
import
com.vj.test.BillingService.RequestPurchase;
import
com.vj.test.BillingService.RestoreTransactions;
import
com.vj.test.Consts.PurchaseState;
import
com.vj.test.Consts.ResponseCode;
public class Mainactivity extends Activity implements OnClickListener,
OnItemSelectedListener
{
private static final String TAG = "Dungeons";
private static final String DB_INITIALIZED
= "db_initialized";
private DungeonsPurchaseObserver mDungeonsPurchaseObserver;
private Handler mHandler;
private BillingService mBillingService;
private Button mBuyButton;
private Spinner mSelectItemSpinner;
private PurchaseDatabase mPurchaseDatabase;
private Cursor mOwnedItemsCursor;
private Set<String> mOwnedItems = new HashSet<String>();
private String mPayloadContents = null;
private static final int DIALOG_CANNOT_CONNECT_ID
= 1;
private static final int DIALOG_BILLING_NOT_SUPPORTED_ID
= 2;
private static final int DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID
= 3;
private enum Managed {
MANAGED,
UNMANAGED, SUBSCRIPTION
}
private class DungeonsPurchaseObserver extends PurchaseObserver
{
public DungeonsPurchaseObserver(Handler handler)
{
super(Mainactivity.this, handler);
}
@Override
public void onBillingSupported(boolean supported, String type) {
if (Consts.DEBUG)
{
Log.i(TAG,
"supported:
"
+ supported);
}
if ((type
== null) || type.equals(Consts.ITEM_TYPE_INAPP))
{
if (supported)
{
restoreDatabase();
mBuyButton.setEnabled(true);
}
else {
showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
}
}
else if (type.equals(Consts.ITEM_TYPE_SUBSCRIPTION))
{
mCatalogAdapter.setSubscriptionsSupported(supported);
}
else {
showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
}
}
@Override
public void onPurchaseStateChange(PurchaseState purchaseState,
String itemId, int quantity, long purchaseTime,
String developerPayload)
{
if (Consts.DEBUG)
{
Log.i(TAG,
"onPurchaseStateChange()
itemId: " + itemId + " "
+
purchaseState);
}
if (developerPayload
== null) {
logProductActivity(itemId,
purchaseState.toString());
}
else {
logProductActivity(itemId,
purchaseState + "\n\t"
+
developerPayload);
}
if (purchaseState
== PurchaseState.PURCHASED)
{
mOwnedItems.add(itemId);
for (CatalogEntry e : CATALOG)
{
if (e.sku.equals(itemId)
&&
e.managed.equals(Managed.SUBSCRIPTION))
{
}
}
}
mCatalogAdapter.setOwnedItems(mOwnedItems);
mOwnedItemsCursor.requery();
}
@Override
public void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode)
{
if (Consts.DEBUG)
{
Log.d(TAG,
request.mProductId + ": " + responseCode);
}
if (responseCode
== ResponseCode.RESULT_OK)
{
if (Consts.DEBUG)
{
Log.i(TAG,
"purchase
was successfully sent to server");
}
logProductActivity(request.mProductId,
"sending
purchase request");
}
else if (responseCode
== ResponseCode.RESULT_USER_CANCELED)
{
if (Consts.DEBUG)
{
Log.i(TAG,
"user
canceled purchase");
}
logProductActivity(request.mProductId,
"dismissed
purchase dialog");
}
else {
if (Consts.DEBUG)
{
Log.i(TAG,
"purchase
failed");
}
logProductActivity(request.mProductId,
"request
purchase returned " + responseCode);
}
}
@Override
public void onRestoreTransactionsResponse(RestoreTransactions request,
ResponseCode responseCode)
{
if (responseCode
== ResponseCode.RESULT_OK)
{
if (Consts.DEBUG)
{
Log.d(TAG,
"completed
RestoreTransactions request");
}
SharedPreferences
prefs = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor
edit = prefs.edit();
edit.putBoolean(DB_INITIALIZED,
true);
edit.commit();
}
else {
if (Consts.DEBUG)
{
Log.d(TAG,
"RestoreTransactions
error: " + responseCode);
}
}
}
}
private static class CatalogEntry {
public String sku;
public int nameId;
public Managed managed;
public CatalogEntry(String sku, int nameId, Managed managed)
{
this.sku = sku;
this.nameId = nameId;
this.managed = managed;
}
}
private static final CatalogEntry[] CATALOG = new CatalogEntry[] {
new CatalogEntry("your product id", R.string.abc123, Managed.MANAGED),
new CatalogEntry("your product id", R.string.abc1234, Managed.MANAGED),
new CatalogEntry("your product id", R.string.ab123200, Managed.MANAGED)
};
private String mItemName;
private String mSku;
private Managed mManagedType;
private CatalogAdapter mCatalogAdapter;
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mHandler = new Handler();
mDungeonsPurchaseObserver = new DungeonsPurchaseObserver(mHandler);
mBillingService = new BillingService();
mBillingService.setContext(this);
mPurchaseDatabase = new PurchaseDatabase(this);
setupWidgets();
ResponseHandler.register(mDungeonsPurchaseObserver);
if (!mBillingService.checkBillingSupported())
{
showDialog(DIALOG_CANNOT_CONNECT_ID);
}
if (!mBillingService
.checkBillingSupported(Consts.ITEM_TYPE_SUBSCRIPTION))
{
showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
}
}
@Override
protected void onStart()
{
super.onStart();
ResponseHandler.register(mDungeonsPurchaseObserver);
initializeOwnedItems();
}
@Override
protected void onStop()
{
super.onStop();
ResponseHandler.unregister(mDungeonsPurchaseObserver);
}
@Override
protected void onDestroy()
{
super.onDestroy();
mPurchaseDatabase.close();
mBillingService.unbind();
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id)
{
case DIALOG_CANNOT_CONNECT_ID:
return createDialog(R.string.cannot_connect_title,
R.string.cannot_connect_message);
case DIALOG_BILLING_NOT_SUPPORTED_ID:
return createDialog(R.string.billing_not_supported_title,
R.string.billing_not_supported_message);
case DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID:
return createDialog(R.string.subscriptions_not_supported_title,
R.string.subscriptions_not_supported_message);
default:
return null;
}
}
private Dialog createDialog(int titleId, int messageId)
{
String helpUrl = replaceLanguageAndRegion(getString(R.string.help_url));
if (Consts.DEBUG)
{
Log.i(TAG,
helpUrl);
}
final Uri
helpUri = Uri.parse(helpUrl);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(titleId)
.setIcon(android.R.drawable.stat_sys_warning)
.setMessage(messageId)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, null)
.setNegativeButton(R.string.learn_more,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface
dialog,
int which)
{
Intent intent = new Intent(Intent.ACTION_VIEW,
helpUri);
startActivity(intent);
}
});
return builder.create();
}
private String replaceLanguageAndRegion(String str) {
if (str.contains("%lang%") || str.contains("%region%")) {
Locale locale = Locale.getDefault();
}
return str;
}
private void setupWidgets()
{
mBuyButton = (Button) findViewById(R.id.buy_button);
mBuyButton.setEnabled(false);
mBuyButton.setOnClickListener(this);
mSelectItemSpinner = (Spinner) findViewById(R.id.item_choices);
mCatalogAdapter = new CatalogAdapter(this, CATALOG);
mSelectItemSpinner.setAdapter(mCatalogAdapter);
mSelectItemSpinner.setOnItemSelectedListener(this);
mOwnedItemsCursor = mPurchaseDatabase.queryAllPurchasedItems();
startManagingCursor(mOwnedItemsCursor);
String[] from = new String[] {
PurchaseDatabase.PURCHASED_PRODUCT_ID_COL,
PurchaseDatabase.PURCHASED_QUANTITY_COL
};
int[] to = new int[] { R.id.item_name, R.id.item_quantity
};
new SimpleCursorAdapter(this, R.layout.item_row, mOwnedItemsCursor,
from,
to);
}
private void prependLogEntry(CharSequence
cs) {
SpannableStringBuilder contents = new SpannableStringBuilder(cs);
contents.append('\n');
}
private void logProductActivity(String product, String activity)
{
SpannableStringBuilder contents = new SpannableStringBuilder();
contents.append(Html.fromHtml("<b>" + product + "</b>:
"));
contents.append(activity);
prependLogEntry(contents);
}
private void restoreDatabase()
{
SharedPreferences
prefs = getPreferences(MODE_PRIVATE);
boolean initialized =
prefs.getBoolean(DB_INITIALIZED, false);
if (!initialized)
{
mBillingService.restoreTransactions();
Toast.makeText(this, R.string.restoring_transactions,
Toast.LENGTH_LONG).show();
}
}
private void initializeOwnedItems()
{
new Thread(new Runnable() {
@Override
public void run()
{
doInitializeOwnedItems();
}
}).start();
}
private void doInitializeOwnedItems()
{
Cursor
cursor = mPurchaseDatabase.queryAllPurchasedItems();
if (cursor == null) {
return;
}
final Set<String> ownedItems = new HashSet<String>();
try {
int productIdCol =
cursor
.getColumnIndexOrThrow(PurchaseDatabase.PURCHASED_PRODUCT_ID_COL);
while (cursor.moveToNext())
{
String productId =
cursor.getString(productIdCol);
ownedItems.add(productId);
}
}
finally {
cursor.close();
}
mHandler.post(new Runnable() {
@Override
public void run()
{
mOwnedItems.addAll(ownedItems);
mCatalogAdapter.setOwnedItems(mOwnedItems);
}
});
}
@Override
public void onClick(View v) {
if (v == mBuyButton) {
if (Consts.DEBUG)
{
Log.d(TAG,
"buying:
"
+ mItemName + " sku: " + mSku);
}
if ((mManagedType != Managed.SUBSCRIPTION)
&&
!mBillingService.requestPurchase(mSku,
Consts.ITEM_TYPE_INAPP,
mPayloadContents)) {
showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
}
else if (!mBillingService.requestPurchase(mSku,
Consts.ITEM_TYPE_SUBSCRIPTION,
mPayloadContents)) {
showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
}
}
}
@Override
public void onItemSelected(AdapterView<?>
parent, View view, int position,
long id) {
mItemName = getString(CATALOG[position].nameId);
mSku = CATALOG[position].sku;
mManagedType = CATALOG[position].managed;
}
@Override
public void onNothingSelected(AdapterView<?>
arg0) {
}
private static class CatalogAdapter extends ArrayAdapter<String> {
private CatalogEntry[] mCatalog;
private Set<String> mOwnedItems = new HashSet<String>();
private boolean mIsSubscriptionsSupported = false;
public CatalogAdapter(Context
context, CatalogEntry[] catalog) {
super(context, android.R.layout.simple_spinner_item);
mCatalog = catalog;
for (CatalogEntry element : catalog)
{
add(context.getString(element.nameId));
}
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
public void setOwnedItems(Set<String> ownedItems)
{
mOwnedItems = ownedItems;
notifyDataSetChanged();
}
public void setSubscriptionsSupported(boolean supported)
{
mIsSubscriptionsSupported = supported;
}
@Override
public boolean areAllItemsEnabled()
{
return false;
}
@Override
public boolean isEnabled(int position)
{
CatalogEntry entry = mCatalog[position];
if ((entry.managed == Managed.MANAGED)
&&
mOwnedItems.contains(entry.sku)) {
return false;
}
if ((entry.managed == Managed.SUBSCRIPTION)
&&
!mIsSubscriptionsSupported) {
return false;
}
return true;
}
@Override
public View getDropDownView(int position, View convertView,
ViewGroup
parent) {
View view = super.getDropDownView(position,
convertView, parent);
view.setEnabled(isEnabled(position));
return view;
}
}
}
Then Add In-App product
Go to Developer account -> Add Apk Signed Apk -> Then you can able to see In-App product Navigation then click -> Add New Product - >Manage Product -> Enter Product ID and Mandatory field -> Then Make it Active Product.
Check the below Screen Shot
*This product ID only you have to add in above the code for every product.
Then you need base64EncodedPublicKey
This key you need to add Security.Java class line no 81
Cehck this screen shot
Copy this key paste it.
Security.Java class full source code
package com.vj.test;
import
java.security.InvalidKeyException;
import
java.security.KeyFactory;
import
java.security.NoSuchAlgorithmException;
import
java.security.PublicKey;
import
java.security.SecureRandom;
import
java.security.Signature;
import
java.security.SignatureException;
import
java.security.spec.InvalidKeySpecException;
import
java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.HashSet;
import org.json.JSONArray;
import
org.json.JSONException;
import org.json.JSONObject;
import
android.text.TextUtils;
import android.util.Log;
import
com.example.dungeons.util.Base64;
import
com.example.dungeons.util.Base64DecoderException;
import
com.vj.test.Consts.PurchaseState;
public class Security {
private static final String TAG = "Security";
private static final String KEY_FACTORY_ALGORITHM
= "RSA";
private static final String SIGNATURE_ALGORITHM
= "SHA1withRSA";
private static final SecureRandom RANDOM = new SecureRandom();
private static HashSet<Long> sKnownNonces = new HashSet<Long>();
public static class VerifiedPurchase {
public PurchaseState purchaseState;
public String notificationId;
public String productId;
public String orderId;
public long purchaseTime;
public String developerPayload;
public VerifiedPurchase(PurchaseState purchaseState,
String notificationId,
String productId, String orderId,
long purchaseTime,
String developerPayload)
{
this.purchaseState = purchaseState;
this.notificationId = notificationId;
this.productId = productId;
this.orderId = orderId;
this.purchaseTime = purchaseTime;
this.developerPayload = developerPayload;
}
}
public static long generateNonce()
{
long nonce = RANDOM.nextLong();
sKnownNonces.add(nonce);
return nonce;
}
public static void removeNonce(long nonce)
{
sKnownNonces.remove(nonce);
}
public static boolean isNonceKnown(long nonce)
{
return sKnownNonces.contains(nonce);
}
public static ArrayList<VerifiedPurchase> verifyPurchase(String signedData,
String signature)
{
if (signedData
== null) {
Log.e(TAG,
"data
is null");
return null;
}
if (Consts.DEBUG)
{
Log.i(TAG,
"signedData:
"
+ signedData);
}
boolean verified = false;
if (!TextUtils.isEmpty(signature))
{
String base64EncodedPublicKey = "Here Your Public KEey";
PublicKey
key = Security.generatePublicKey(base64EncodedPublicKey);
verified
= Security.verify(key, signedData,
signature);
if (!verified)
{
Log.w(TAG,
"signature
does not match data.");
return null;
}
}
JSONObject jObject;
JSONArray jTransactionsArray =
null;
int numTransactions = 0;
long nonce = 0L;
try {
jObject
= new JSONObject(signedData);
nonce
= jObject.optLong("nonce");
jTransactionsArray
= jObject.optJSONArray("orders");
if (jTransactionsArray
!= null) {
numTransactions
= jTransactionsArray.length();
}
}
catch (JSONException e) {
return null;
}
if (!Security.isNonceKnown(nonce))
{
Log.w(TAG,
"Nonce
not found: " + nonce);
return null;
}
ArrayList<VerifiedPurchase> purchases = new ArrayList<VerifiedPurchase>();
try {
for (int i = 0; i <
numTransactions; i++) {
JSONObject jElement =
jTransactionsArray.getJSONObject(i);
int response = jElement.getInt("purchaseState");
PurchaseState purchaseState = PurchaseState.valueOf(response);
String productId =
jElement.getString("productId");
@SuppressWarnings("unused")
String packageName =
jElement.getString("packageName");
long purchaseTime =
jElement.getLong("purchaseTime");
String orderId = jElement.optString("orderId", "");
String notifyId = null;
if (jElement.has("notificationId")) {
notifyId
= jElement.getString("notificationId");
}
String developerPayload =
jElement.optString(
"developerPayload", null);
if ((purchaseState
== PurchaseState.PURCHASED)
&& !verified) {
continue;
}
purchases.add(new VerifiedPurchase(purchaseState,
notifyId,
productId,
orderId, purchaseTime, developerPayload));
}
}
catch (JSONException e) {
Log.e(TAG,
"JSON
exception: ", e);
return null;
}
removeNonce(nonce);
return purchases;
}
public static PublicKey generatePublicKey(String encodedPublicKey)
{
try {
byte[] decodedKey = Base64.decode(encodedPublicKey);
KeyFactory keyFactory = KeyFactory
.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory
.generatePublic(new X509EncodedKeySpec(decodedKey));
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
catch (InvalidKeySpecException e) {
Log.e(TAG,
"Invalid
key specification.");
throw new IllegalArgumentException(e);
}
catch (Base64DecoderException e) {
Log.e(TAG,
"Base64
decoding failed.");
throw new IllegalArgumentException(e);
}
}
public static boolean verify(PublicKey
publicKey, String signedData,
String signature)
{
if (Consts.DEBUG)
{
Log.i(TAG,
"signature:
"
+ signature);
}
Signature
sig;
try {
sig
= Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(Base64.decode(signature)))
{
Log.e(TAG,
"Signature
verification failed.");
return false;
}
return true;
}
catch (NoSuchAlgorithmException e) {
Log.e(TAG,
"NoSuchAlgorithmException.");
}
catch (InvalidKeyException e) {
Log.e(TAG,
"Invalid
key specification.");
}
catch (SignatureException e) {
Log.e(TAG,
"Signature
exception.");
}
catch (Base64DecoderException e) {
Log.e(TAG,
"Base64
decoding failed.");
}
return false;
}
}
Then publish the application
Wait for several hours.
Then download application enjoy with In-App Billing
Meine Hände wanderten in ihren Schritt und ich begann Sie zu
ReplyDeletefummeln. Der Mensch ist ein Gewohnheitstier. Jedoch wenn du mir unverschämt wirst, fahre ich unsere
Krallen aus und kratze ebenso schon mal.
Besuchen Sie auch meine Seite live strip cam
Ich stehe darаuf, gebumst zu dürften, bis ich einеn Höhepunkt nach ԁieѕem anderen bekomme.
ReplyDeleteWillst du еbenfallѕ einmal meinen hеißen Arѕch nehmen und mich
in meinen Hinteгn νerwöhnen. Ich hаbe schon unzählige
Kеrlе glücklich gemасht und miсh vοn
viеlеn Herren bumsen lassen.
Moechteѕt du dir mеine Pаge ansehen? kleinanzeigen österreich
Hi I am so thrilled I found your blog page, I really found you by accident,
ReplyDeletewhile I was browsing on Google for something else, Regardless I am here now and would
just like to say thanks a lot for a remarkable post and a all
round exciting blog (I also love the theme/design), I
don't have time to look over it all at the moment but I have book-marked it and also added your RSS feeds, so when I have time I will be back to read a lot more, Please do keep up the awesome work.
Visit my web-site Louis vuitton pas Cher
I have done these tutorial today. Its really amazing to integrated a third party app into one app. Thanks for sharing.
ReplyDeleteIn future,Android application development should be good level compared to other fields.Because Android application plays an important things in people life.
ReplyDeleteWebsite Development Companies Bangalore
This is the clear lesson of all.
ReplyDeleteBut I do not understand how this script access to the full application and all buttons after purchase.
Please reply to email weeeeeez@yandex.ru
Thanks for sharing this tutorial
ReplyDeleteNow the process of Android application development is very difficult.why i saying like this because,more competition going on this field.
ReplyDeleteOutsourcing Website Designer
A well trained android developers should be having ability to make the useful mobile application to people.
ReplyDeleteWeb Design Companies | Web Designing Company
It is really such an informative blog, thanks for the post.
ReplyDeleteWeb Design Services India | Web Design Companies Bangalore
Thanks for sharing the useful coding. Really it is comprehensive.
ReplyDeleteSEO Company in Bangalore |SEO Agency in Bangalore | Best SEO Company in Bangalore |Web Development Company in Bangalore |Digital Marketing Company in Bangalore |Web Development Company in Bangalore |Ecommerce Website Development Company in Bangalore |Ecommerce Website Development Company in Bangalore|Mobile App Development Company in Bangalore
This comment has been removed by the author.
ReplyDeleteHy vọng các bạn sẽ có nhiều bài viết hay và thú vị hơn. Xin cảm ơn rất nhiều
ReplyDeletechó Bull Pháp
bán chó bull pháp
chó bull pháp giá bao nhiêu
mua chó bull pháp
Aquest és un dels millors articles de la història. Gràcies per compartir. Li desitjo sort i èxit!
ReplyDeletegiảo cổ lam 5 lá
giảo cổ lam 7 lá
giảo cổ lam khô
giảo cổ lam 9 lá
Thật tuyệt vời
Deletecase máy tính cũ
vga cũ hà nội
mua bán máy tính cũ hà nội
Lắp đặt phòng net trọn gói
ok mà anh ơi
DeleteDịch vụ vận chuyển chó mèo cảnh Sài Gòn Hà Nội
Chuyên dịch vụ phối giống chó Corgi tại Hà Nội
Quy trình phối giống chó Bull Pháp
Ji kerema xwe birêvebirê hevpeymanê hevpar Dixwaze hûn gotarên balkêş û balkêş in.
ReplyDeletelều xông hơi
lều xông hơi giá rẻ
lều xông hơi sau sinh
lều xông hơi loại nào tốt
Your post is just outstanding! thanks for such a post, its really going great work.
ReplyDeleteWebsite Designing Company in Delhi
Göreviniz benim için çok önemli. Teşekkürler ve mutluluklar dilerim.
ReplyDeleteLều xông hơi khô
Túi xông hơi cá nhân
Lều xông hơi hồng ngoại
Mua lều xông hơi
This comment has been removed by the author.
ReplyDeleteok
ReplyDeleteTrị dứt điểm bệnh viêm xoang bằng máy xông hương tinh dầu cao cấp
Công ty phân phối máy khuếch tán tinh dầu Hà Nội uy tín chất lượng
Máy khuếch tán tinh dầu Chery A07 cải tạo không khí thân thiện môi trường
Công dụng của máy khuếch tán tinh dầu- bạn có thể tham khảo
Yes
Deletehttps://cic.com.vn/forums/member.php?action=profile&uid=94904
http://inktalks.com/people/ngoctuyenpcmaytinh
https://www.dohtheme.com/community/members/ngoctuyenpc.10057/
https://expo.io/@ngoctuyenpc
أفضل الأشياء سوف تأتي إليك وأحبائك. شكرا لتقاسم!
ReplyDeleteTư vấn nên chọn lều xông hơi loại nào tốt
Lều xông hơi sau sinh
Tiệm bán lều xông hơi giá rẻ tại Hà Nội
túi xông hơi cá nhân, bạn đồng hành của mọi nhà.
Vanskeligheter( van bi ) vil passere. På samme måte som( van điện từ ) regnet utenfor( van giảm áp ) vinduet, hvor nostalgisk( van xả khí ) er det som til slutt( van cửa ) vil fjerne( van công nghiệp ) himmelen.
ReplyDeleteNhững thông tin bạn chia sẻ quá tuyệt vời
ReplyDeletemáy phun tinh dầu
máy khuếch tán tinh dầu tphcm
máy khuếch tán tinh dầu hà nội
máy xông phòng ngủ
Hay quá
Deletethanh lý phòng net
màn hình máy tính 24 inch cũ
lắp đặt phòng net
giá card màn hình
Bài viết quá ok
ReplyDeleteTRIỆU CHỨNG TIỂU ĐƯỜNG
TIỆM BÁN METHI ẤN ĐỘ HÀ THÀNH
CỬA HÀNG BÁN HẠT ME THI HÀ NỘI TỐT
ĐÁI THÁO ĐƯỜNG VÀ NHỮNG ĐIỀU CẦN BIẾT
HẠT METHI MUA Ở ĐÂU HÀ NỘI TỐT?
ok hay
ReplyDeleteCửa lưới chống muỗi
Cửa lưới chống muỗi Hà Nội
Những thông tin bạn chia sẻ quá hay
ReplyDeletecáo tuyết
cáo tuyết thái lan
Mua cáo tuyết
Bán cáo tuyết
bull pháp hà nội
ok thank
ReplyDeletehttps://duongtinhangngay.blogspot.com/2019/09/moi-lan-xong-tinh-dau-nen-dung-bao.html
Mỗi lần xông tinh dầu nên dùng bao nhiêu giọt là tốt?
Trong tương lai bạn sẽ có nhiều bài viết hay hon
ReplyDeletehttps://www.flipsnack.com/cualuoihm/
https://creativemarket.com/cualuoihm
https://pastebin.com/u/cualuoihm
Tôi tin bạn sẽ có nhiều bài viết hay hơn
ReplyDeletehttps://forums.pokemmo.eu/index.php?/profile/131787-cualuoihm/
https://doremir.com/forums/profile/cualuoihm
https://www.wincert.net/forum/profile/100889-cualuoihm/
https://www.goodreads.com/user/show/104133368-cualuoihm
Tôi thích những chia sẻ
ReplyDeletehttps://expo.io/@maykhuechtantinhdau
https://www.dohtheme.com/community/members/maykhuechtantinhdau.7575/
http://inktalks.com/people/maykhuechtantinhdau
https://cic.com.vn/forums/member.php?action=profile&uid=94275
https://www.pinterest.com/chien0208924/
Những điều may mắn sẽ đến với bạn và những người thân yêu
ReplyDeletehttps://www.okeynotes.com/banchocanh
https://www.f6s.com/banchocanh
https://www.ioby.org/users/anhnguyenvanchien020892373478
https://pro.ideafit.com/profile/ban-chocanh
ok đấy
ReplyDeleteDịch vụ vận chuyển chó mèo cảnh Sài Gòn Hà Nội
It is amazing and wonderful to visit your Blog.Thanks for sharing this information,this is useful to us. Keep posting!
ReplyDeleteHello,
ReplyDeleteI love your blog so much. i would like to inform you that there's plenty of good information on this blog, I loved to read it and I think people will get a lot more support from this blog. Thanks for sharing this informative blog, please keep it up and share some special posts with us in the future.
about us
Mobile app development companies in Bangalore
we are also best Mobile app development company in Bangalore
Mobile app development companies in India
Mobile app development companies in India
This is a very good article and nice collection of information , would love to read more of such blogs and also know our services
ReplyDeleteMobile application development in India
Mobile application development in Delhi
Bài viết của bạn hết sức thú vị
ReplyDeletemáy xông tinh dầu bằng điện
máy khuếch tán tinh dầu silent night
máy xông tinh dầu đuổi muỗi
máy khuếch tán tinh dầu hà nội
This is the kind of information that i was looking and i got after reading this blog post and thanks for sharing
ReplyDeletemobile app development company in bangalore
mobile app development company in dubai
mobile app development company in usa
mobile app development company in india
thú vị
ReplyDeleteSự thật về đông trùng hạ thảo Việt Nam bạn nên biết
https://dongtrunghathaonepa.com/
Giải mã bí mật đông trùng hạ thảo là gì?
Tác dụng đông trùng hạ thảo
Thanks for your great post we are the leading digital marketing agencies in manchester and seo agencies in manchester
ReplyDeleteA well informative blog which speaks about Mobile applications hands-on experience like these helps us to become an Android application developer.
ReplyDeleteNice post & thanks for sharing
ReplyDeletemobile app development company
web app development company
cloud app development company
saas app development company
custom mobile app & web app development company
mobile app and web app maintenance services
seo services | seo company
digital marketing company
Paid marketing services | ppc service providers
Hey Nice Blog!! Thanks For Sharing!!!Wonderful blog & good post.Its really helpful for me, waiting for a more new post. Keep Blogging!
ReplyDeleteindustrial automation systems
bockchain development
Hello, I read your blog and find it to be very informative about Personal and Social Website Design. Please keep up the good work and continue to share more unique posts. I have a lot of good stuff I'd like to share, and I'm hoping that this blog will provide a lot of help to people. Thank you for sharing this informative blog.
ReplyDeletee learning & LMS app development company in India
e learning & LMS app development company in UK
e learning & LMS app development company in USA
e learning & LMS app development company in Dubai, UAE
e learning & LMS app development company in Kolkata
e learning & LMS app development company in Chennai
e learning & LMS app development company in Hyderabad
e learning & LMS app development company in New Delhi
e learning education & LMS app development company in Mumbai
HI guys,
ReplyDeleteThis is a very good post, and I like it very much. For us, it's insightful and helpful. For a long time, I've been looking for this and I'm just really pleased to say that I'm working with this stuff as well. Thanks for sharing this with us.
Digital Marketing Company in Jaipur
Digital Marketing company In Delhi
Digital Marketing Company in Bangalore
SEO Company in Jaipur
Website development Company in Jaipur
PPC Company in Jaipur
Digital Marketing Company in USA
Những chia sẻ quá hay mà a
ReplyDeleteMách bạn chi phí thi công nội thất trọn gói tại hà nội
Chi phí thi công nội thất trọn gói bao nhiêu năm 2021
Khám phá chi phí thi công nội thất trọn gói bao nhiêu
Nice Post!
ReplyDeletesports betting app development
Kết thúc chuyện tình yêu mấy
ReplyDeletemàn hình máy tính 2k
lắp đặt phòng net trọn gói
lắp đặt phòng net
lắp đặt phòng game
Nhìn cx dc
ReplyDeleteMua quà tặng doanh nghiệp ở đâu tốt?
Bí quyết chọn quà tặng doanh nghiệp tốt
Cửa hàng làm kỷ niệm chương tốt
Tuyệt chiêu chọn quà tặng thành công
Thanks for your great post.We are the leading seo company in mumbai. Hire our seo agency in mumbai today for seo services in mumbai
ReplyDeleteNice post really useful information. We are the leading Ecommerce Website Designers in Bangalore . Hire our ecommerce website designers in bangalore for ecommerce website development services.
ReplyDeletethanks for sharing this.
ReplyDeleteget the best blog to read -https://lilacinfotech.com/blog/90/top-android-app-development-trends-to-lookout-in-2021
Tìm lại người xưa
ReplyDeleteCạo vôi răng thông thường
Điều mà anh chia sẻ tôi thực sự thích
ReplyDeleteWhat is PP (Polypropylene)? Its Application In our Life
Learn more about FIBC bags
What is Flexo printing technology? Why did FIBC manufacturers choose this technology?
Thật hay
ReplyDeleteNhựa PTFE
bạc hợp kim đồng
Trục con lăn
Nhựa UHMW PE
Nhựa PA6
khét
ReplyDeletebao fibc
bao jumbo 1000kg
công ty bao bì jumbo
Khá quá mà
ReplyDeletepp jumbo bag scrap
công ty bao bì jumbo
type a fibc
This is genuinely an awesome read for me. I have bookmarked it and I am anticipating perusing new articles. Keep doing awesome!
ReplyDeleteSEO Company Pune
SEO Pune
Hey, Thanks for asking.
ReplyDeleteDid you know that there are limited number of global biomedical researchers and scientists to biospecimen suppliers across the globe.
Cancer Samples
FFPE Samples
Human Specimens
Thanks for sharing, information is really useful, keep posting.
ReplyDeleteGet in touch with us for..
Best office furniture
furniture dealership
furniture franchise India
Good Blog, Such a Nice Content, Informative for Readers Keep Posting.
ReplyDeletecustom application development services
app development company
Hú hí
ReplyDeleteHé lộ bí mật kỹ thuật đánh cát golf dành cho người mới
Khái quát về diện tích sân golf 18 lỗ tiêu chuẩn
Grip gậy golf là gì – Tiêu chí lựa chuẩn grip gậy đánh golf chuẩn 2022
Gậy golf
https://shopgolf247.com/
Great article. Keep writing such kind of information on your blog.
ReplyDeleteUnified Communications
Tôi xin cảm ơn
ReplyDeletelắp thang máy gia đình bao nhiêu tiền
thang máy chung cư mini
Thang máy Delta